1.谈谈你对static关键字的理解?
1.1static的特点
(1)static是修饰符,用于修饰成员。
(2)static修饰的成员被所有对象所共享。
(3)static优于对象存在,因为static修饰的成员已经随着类的加载已经存在了。
(4)static修饰的成员多了一种调用方式,可以直接被类名所调用,类名.静态成员。
(5)static修饰的成员是共享数据,对象中的存储是特有数据。
1.2static变量
(1)按照是否静态对类成员变量进行分类,一种是被static修饰的成员变量,叫静态变量或者类变量,一种是没有被static修饰的变量,叫做实例变量。
区别:静态变量在内存中只有一个拷贝,JVM只为静态分配一次内存,在过类加载的过程完成静态变量的内存分配。可类名.静态成员直接访问,也可以通过对象来访问(不推荐)
实例变量,每创建一个实例,就会为实例变量分配一次内存,内存中实例变量可以在内存中存在多个拷贝,但互不影响。
(2)static成员变量的初始化按照定义的顺序进行初始化,static不能修饰局部变量。
(3)以下两种情况使用静态变量。
在对象之间共享值时;
方便访问变量时;
1.3静态方法
静态方法不用生成类的实例就可以直接调用,即static修饰的成员不在归所对象所有,而是属于类,可以理解为共享的,可以通过
类名直接访问,不用消耗资源反复创建对象,因为在类加载的时候已经在内存中,直到程序结束才会被内存释放,没有用static修饰的成员在使用完后就会被内存回收。
1.4static修饰类
一般静态内部类可以用static修饰,(静态内部类一般可以分为四种:常规内部类、静态内部类、局部内部类、匿名内部类),只能访问外部类的static成员,不能直接访问外部类的实例方法和实例变量。
1.5static代码块
static静态代码块在类中独立于类成员的static语句块,可以有多个,位置随便放,不在任何的方法体内,JVM在加载类的时候会执行这些语句块,如果有多个,会按照顺序执行,每个代码块被执行一次,可以用来优化程序性能。
1.6static方法和Static代码块的区别
静态代码块是自动执行的
静态方法是被调用才执行的
静态方法只能直接调用同类的静态成员变量,不能访问类中的非静态成员变量。对于非静态成员变量和方法,只能通过创建类的实例对象来访问,而静态方法在使用前不需要创建任何对象(静态变量属于整个类,而不属于某个对象)
静态方法不能以任何方式调用this和super关键字,因为静态方法在使用前不创建任何实例对象,当静态方法调用时,this所引用的象对没有产生。(this关键字用来表示当前对象本身,或当前类的一个实例,通过this可以调用本对象的所有属性和方法,成员变量和方法内部的变量重名时,只能使用this)
2.谈谈类加载过程
类加载就是JVM把class文件加载到内存,并对数据校进行校验、准备、解析、初始化,最终形成JVM可以直接使用的类型的过程。
类加载的过程主要分为三部分:加载、链接、初始化。
链接又分为三个部分:验证、准备、解析。
2.1加载
把class字节码文件从各个源通过类加载器加载到内存中。
字节码文件来源:从本地路径下编译生成的.class文件,从jar包中的.class文件。从远程网络,以及动态代理实时编译。
类加载:一般包启括动类加载器、扩展类加载器、应用类加载器、用户的自定义类加载器。
启动类加载器:加载JAVA_HOME\jre\lib或-Xbootclasspath参数指定目录下的类库,我们无法获得启动类加载器,在Hotspot中, 启动类加载器是JVM的一部分。所有其他类加载器都是独立于JVM之外的,并且需要启动类加载器加载。
扩展类加载器:负责加载JAVA_HOME\lib\jre\ext或者Java.ext.dirs系统变量指定目录下的类库。
应用类加载器:负责加载classpath下的类。
自定义加载器:防止代码反编译,对代码进行加密,自定义类加载器解密加密的代码。当字节码文件来自于不同源,通过自定义
类加载器从指定的来源加载类。
2.2链接
将加载到JVM中的二进制字节流文件合并到JVM的运行状态中。包括验证、准备、解析。
(1)验证数据信息是否符合JVM规范。是否是有效的字节码文件,验证内容包含类数据信息的格式验证、语义分析、验证操作。
格式验证:验证是否符合class文件规范。
语义验证:验证是否有被final修饰的类被继承。类中的final方法是否被子类重写。子类和父类是否有冲突,出现不合理的重载。
操作验证:在操作数栈中的数据必须进行正确的操作,对常量池中的各种符号引用执行验证。
(2)准备
准备为类变量即静态变量分配内存,赋予初值(实例变量还没产生对象,不包括)。初始值不是代码中具体写的初始化的值,而是Java虚拟机根据不同变量类型默认的初始值。final修饰的静态变量直接赋予原值。
(3)解析
将常量池中的符号引用转换为直接引用。
符号引用:一个字符串,能够唯一识别一个类、一个方法、一个变量的相关信息。
直接引用:可以理解为一个内存地址,偏移量。例如一个类、方法、字段在内存中的指针和偏移量。
一些静态绑定的会被解析。静态绑定包括final方法、static方法、构造器。动态绑定的在运行的时候才会解析。
2.3初始化
对类变量进行初始化的,执行类构造器的过程。
其实是对static修饰的变量和语句进行初始化。
初始化一个类,按照父类子类顺序初始。
同时存在多个静态变量和静态代码块,按顺序从上向下执行。
3.为什么要有类加载器?自定义类加载器如何实现?
3.1为什么要有类加载器
类加载器就是负责把Java类中的字节码文件加载到虚拟机中
3.2自定义类加载器如何实现
自定义的类加载器只需要继承ClassLoader,并覆盖findClass方法即可。
4.什么是双亲委派模型?为什么要遵循双亲委派模型?
4.1 双亲委派模型
如果一个类加载器收到加载类的请求,他首先不会自己自己尝试加载这个类,而是把加载请求委派给父类加载器完成,每个类加载器都是如此,只有当父类加载器在自己的搜索范围内找不到加载的类时,子类加载器才会尝试自己去加载这个类。
4.2 原因
(1)安全,避免用户自己编写的动态类替换Java的核心类。
(2)避免类重复加载。JVM区分不同类,不仅仅根据类名(类加载实例+包名+类名),相同的类文件被不同的类加载器加载就是不同的两个类。