java基础提高1--反射、安全失败、深浅拷贝...

一 、 数据库三大范式和反范式

1. 详见: https://blog.csdn.net/chenyyhh92/article/details/51174343

  • 1NF、列不可分;(例如地址字段,我们应该分成多个字段显示,保证原子性)

  • 2NF、不存在部分依赖;(把没关系的几个字段放到同一个表当中,我们应该把它们分开来)

  • 3NF、不存在传递依赖。(需要另外一个表的数据,只需要引入另一个表的主键即可)

2. 反三范式

  • 概念:没有冗余的数据库未必是最好的数据库,有时为了提高运行效率,提高读性能,就必须降低范式标准适当保留冗余数据
  • 做法: 在概念数据模型设计时遵守第三范式,降低范式标准的工作放到物理数据模型设计时考虑。降低范式就是增加字段,减少了查询时的关联,提高查询效率,因为在数据库的操作中查询的比例要远远大于DML的比例。但是反范式化一定要适度,并且在原本已满足三范式的基础上再做调整的。




二、快速失败和安全失败

https://blog.csdn.net/chenssy/article/details/38151189

1. 快速失败(fail-fast)

概念:在使用迭代器对集合对象进行遍历的时候,如果 A 线程正在对集合进行遍历,此时 B 线程对集合进行修改(增加、删除、修改),或者 A 线程在遍历过程中对集合进行修改,都会导致 A 线程抛出 ConcurrentModificationException 异常。
错误原因:底层ArrayList迭代代码中,它增删改都会先判断当前(迭代时)集合长度是否和迭代之前长度相等,否者抛出这个异常。

    public static void a(){
        //1. 创建集合并存入数据
        List list = new ArrayList();
        for(int i = 0 ; i < 10 ; i++){
            list.add(i);
        }

        //2. 遍历出集合中所有值
        for (Object i:list){
            System.out.println("遍历:"+i);
            //2.1 判断:如果集合中元素为3时,删除该元素
            if(i.equals(3)){
                list.remove(i);//造成快速失败的原因
            }
        }
    }

执行后的效果如下图:
java基础提高1--反射、安全失败、深浅拷贝..._第1张图片

2. 安全失败(fail-safe)

使用CopyOnWriteArrayList来替换ArrayList。推荐使用该方案。
原理:

  1. 任何对array在结构上有所改变的操作(add、remove、clear等),CopyOnWriterArrayList都会copy现有的数据,再在copy的数据上修改,这样就不会影响COWIterator中的数据了,修改完成之后改变原有数据的引用即可。
  2. 同时这样造成的代价就是产生大量的对象,同时数组的copy也是相当有损耗的。
   public static void b(){
        //1. 创建集合并存入数据
        List list = new CopyOnWriteArrayList();
        for(int i = 0 ; i < 10 ; i++){
            list.add(i);
        }

        //2. 遍历出集合中所有值
        for (Object i:list){
            System.out.println("遍历:"+i);
            //2.1 判断:如果集合中元素为3时,删除该元素
            if(i.equals(3)){
                list.remove(i);
            }
        }
    }

java基础提高1--反射、安全失败、深浅拷贝..._第2张图片

注意:

  1. Iterator的安全失败是基于对底层集合做拷贝,因此,它不受源集合上修改的影响。
  2. java.util包下面的所有的集合类都是快速失败的。快速失败的迭代器会抛出ConcurrentModificationException异常。当在迭代一个集合的时候,如果有另外一个线程在修改这个集合,就会抛出ConcurrentModification异常。不能在多线程下发生并发修改。
  3. 查看ArrayList源代码,在next方法执行的时候,会执行checkForComodification()方法




三、Class.forName(“XXX”) 、.class和.getClass()的作用

http://www.cnblogs.com/Seachal/p/5371733.html

https://blog.csdn.net/sinat_38259539/article/details/71799078 (详细介绍怎么使用反射来运行java类)

1、Java反射

概念:

  1. 在运行时期获取对象类型信息的操作。
  2. 传统的编程方法要求程序员在编译阶段决定使用的类型,但是在反射的帮助下,编程人员可以动态获取这些信息,从而编写更加具有可移植性的代码。
  3. 严格地说,反射并非编程语言的特性,因为在任何一种语言都可以实现反射机制,但是如果编程语言本身支持反射,那么反射的实现就会方便很多。

2、Class.forName(“XXX”) 、getClass()和.class :获得Class对象

概念:

  1. jvm中有N多的实例每个类都有该Class对象。
  2. Class 没有公共构造方法。Class 对象是在加载类时由 Java 虚拟机以及通过调用类加载器中的defineClass 方法自动构造的。也就是这不需要我们自己去处理创建,JVM已经帮我们创建好了。

注意:在运行期间,一个类,只有一个Class对象产生。

package sdasd;
/**
 * 获取Class对象的三种方式
 * 1 Object ——> getClass();
 * 2 任何数据类型(包括基本数据类型)都有一个“静态”的class属性
 * 3 通过Class类的静态方法:forName(String  className)(常用)
 *
 */
public class Fanshe {
	public static void main(String[] args) {
		//第一种方式获取Class对象  
		Student stu1 = new Student();//这一new 产生一个Student对象,一个Class对象。
		Class stuClass = stu1.getClass();//获取Class对象
		System.out.println(stuClass.getName());
		
		//第二种方式获取Class对象
		Class stuClass2 = Student.class;
		System.out.println(stuClass == stuClass2);//判断第一种方式获取的Class对象和第二种方式获取的是否是同一个
		
		//第三种方式获取Class对象(常用)
		try {
			Class stuClass3 = Class.forName("sdasd.Student");//注意此字符串必须是真实路径,就是带包名的类路径,包名.类名
			System.out.println(stuClass3 == stuClass2);//判断三种方式是否获取的是同一个Class对象
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
		
	}
}

java基础提高1--反射、安全失败、深浅拷贝..._第3张图片
获得Class对象的信息

  • getName():String:获得该类型的全称名称。
  • getSuperClass():Class:获得该类型的直接父类,如果该类型没有直接父类,那么返回null。
  • getInterfaces():Class[]:获得该类型实现的所有接口。
  • isArray():boolean:判断该类型是否是数组
  • isEnum():boolean:判断该类型是否是枚举类型。
  • getFields():获得某个类的所有的公共(public)的字段,包括父类中的字段。
  • getDeclaredFields():获得某个类的所有声明的字段,即包括public、private和proteced,但是不包括父类的申明字段。
  • … …

3、Class.forName().newInstance():动态加载类(另一种new对象的方法)

    public static void a() throws ClassNotFoundException, IllegalAccessException, InstantiationException {
        c c1 = new c();
        //这里面参数是完整的类路径
        //Class.forName(xxx.xx.xx) 返回的是一个类,
        //.newInstance() 后才创建一个对象 
        //Class.forName(xxx.xx.xx);的作用是要求JVM查找并加载指定的类,也就是说JVM会执行该类的静态代码段
        Object c2 = Class.forName("sdasd.c").newInstance();
        System.out.println("c对象:" + c1);
        System.out.println("c1对象:" + c2);
    }

与new 对象的区别:

  1. Class.forName()在运行时加载;
    Class.class和getClass()是在编译时加载.
  2. new ClassName(),就是所谓的静态加载,
    Class.forName(“ClassName”),就是所谓的动态加载。
  3. “静态加载”的类在编译的时候就要提供,而动态加载的类在源程序编译时可以缺席,在运行时按需提供

4、Class对象的生成的原理

public static void main(String[] args)  throws ClassNotFoundException {
       // 测试.class
       //       1个都不调用
       Class testTypeClass = TestClassType.class;
       System.out.println("测试.class:" + testTypeClass);
       System.out.println();

       // 测试Class.forName()
       //       只会调用 static代码块
       Class testTypeForName = Class.forName("sdasd.TestClassType");
       System.out.println("测试Class.forName():" + testTypeForName);
       System.out.println();

       // 测试Object.getClass()
       //       3个代码块都会调用
       TestClassType testTypeGetClass = new TestClassType();
       System.out.println("测试Object.getClass():" + testTypeGetClass.getClass());
}


class  TestClassType {

    // 构造函数
    public TestClassType() {
        System.out.println("---构造函数---");
    }

    // 静态的参数初始化
    static {
        System.out.println("---静态的参数初始化---");
    }

    // 非静态的参数初始化
    {
        System.out.println("---非静态的参数初始化---");
    }

}

运行结果;
java基础提高1--反射、安全失败、深浅拷贝..._第4张图片
总结:

  1. 类名.class: JVM将使用类装载器, 将类装入内存(前提是:类还没有装入内存),不做类的初始化工作.返回Class的对象
  2. Class.forName(“类名字符串”): (注:类名字符串是包名+类名) 装入类,并做类的静态初始化,返回Class的对象
  3. 实例对象.getClass(): 对类进行静态初始化、非静态初始化;返回引用运行时真正所指的对象(因为:子对象的引用可能会赋给父对象的引用变量中)所属的类的Class的对象

生成Class对象的过程:

  • 当我们编写一个新的java类时,JVM就会帮我们编译成class对象,存放在同名的.class文件中。在运行时,当需要生成这个类的对象,JVM就会检查此类是否已经装载内存中。若是没有装载,则把.class文件装入到内存中。若是装载,则根据class文件生成实例对象。




五、o(1), o(n), o(logn), o(nlogn)

https://blog.csdn.net/Mars93/article/details/75194138

作用:这是算法的时空复杂度的表示。

  • o(1):最低的时空复杂度了,也就是耗时/耗空间与输入数据大小无关,无论输入数据增大多少倍,耗时/耗空间都不变; 哈希算法就是典型的O(1)时间复杂度,无论数据规模多大,都可以在一次计算后找到目标
  • o(n):就代表数据量增大几倍,耗时也增大几倍;比如常见的遍历算法。
  • o(logn):当数据增大n倍时,耗时增大logn倍;二分查找就是O(logn)的算法,每找一次排除一半的可能,256个数据中查找只要找8次就可以找到目标。
  • o(nlogn):n乘以logn,当数据增大256倍时,耗时增大256*8=2048倍;这个复杂度高于线性低于平方。归并排序就是O(nlogn)的时间复杂度

复杂度排序:o(1) < o(n) < o(logn) < o(nlogn)




六、深拷贝浅拷贝

https://blog.csdn.net/baiye_xing/article/details/71788741

浅拷贝: 浅拷贝仅仅复制所拷贝的对象,而不复制它所引用的对象

深拷贝: 深拷贝把要复制的对象所引用的对象都复制了一遍。(不再指向同一个对象的引用)

检验深拷贝成功:改变任意一个新对象/数组中的属性/元素, 都不改变原对象/数组

你可能感兴趣的:(java基础)