一. 枚举的定义特点以及常用方法
(一) 枚举类型中的常用方法
1. ordinal(): 获取枚举类型中的枚举序数,序数根据定义的枚举项,从0开始,返回值int
2. compareTo(E o) : 比较枚举项之间的顺序大小,方法调用枚举项的序数减去参数枚举项的序数
3. name() : 将枚举项转换成String类型
4. toString() : 将枚举项转换成String类型
5. static <T> T valueOf(Class<T> type,String name) 是Enum类的静态方法,获取指定枚举类中的指定名称的枚举值
6. static values() : 将一个枚举类型中的所有枚举项获取到,返回值类型枚举类型的数组
public enum MyEnum { ONE,TWO,THREE,FOUR; }
public class EnumMethod { public static void main(String[] args) { // 1.ordinal(): 获取枚举类型中的枚举序数,序数根据定义的枚举项,从0开始,返回值int System.out.println(MyEnum.ONE.ordinal());// 0 System.out.println(MyEnum.THREE.ordinal());// 2 // 2.compareTo(E o) : 比较枚举项之间的顺序大小,方法调用枚举项的序数减去参数枚举项的序数 // 0 - 3 System.out.println(MyEnum.ONE.compareTo(MyEnum.FOUR));// -3 /* 3.name() : 将枚举项转换成String类型 4.toString() : 将枚举项转换成String类型*/ System.out.println(MyEnum.TWO.name());// TWO System.out.println(MyEnum.TWO.toString());// TWO //5.static <T> T valueOf(Class<T> type,String name) 是Enum类的静态方法,获取指定枚举类中的指定名称的枚举值 MyEnum my1 = MyEnum.valueOf("THREE"); System.out.println(my1);// THREE System.out.println("---------------------"); MyEnum my2 = MyEnum.valueOf(MyEnum.class,"ONE"); System.out.println(my2);// ONE // 6.static values() : 将一个枚举类型中的所有枚举项获取到,返回值类型枚举类型的数组 MyEnum[] arr = MyEnum.values(); for(MyEnum my : arr){ System.out.println(my); } } }
二. 反射
(一) 虚拟机类加载机制
2.1 虚拟机类加载机制概述
虚拟机把描述类的数据从class文件加载到内存,并对数据进行校验,转换解析和初始化,最终形成可以被java虚拟机直接使用的java类型,这就是虚拟机的类加载机制.
2.2 类加载过程
当程序要使用某个类时,如果该类还未被加载到内存中,系统会通过加载,连接,初始化三步来实现对这个类的加载.
1. 加载
(1) 就是指将class文件读入内存,并为之创建一个Class对象.
任何类被使用时系统都会建立一个Class对象
2. 连接
(1) 验证是否有正确的内部结构,并和其他类协调一致
(2) 准备负责为类的静态成员分配内存,并设置默认初始化值
(3) 解析将类的二进制数据中的符号引用替换为直接引用
3. 初始化
主要对类变量进行初始化
a: 类还未被加载, 程序先加载并连接该类
b: 如该类还有直接父类, 则先初始化其直接父类
c: 有初始化语句,按顺序执行
2.3 类的初始化时机
什么时候这个类的字节码文件被加载?【什么时候创建出该类的字节码对象 Class对象】
1.创建类的实例
2.类的静态成员使用
3.类的静态方法调用
4.使用反射方式来强制创建某个类或接口对应的java.lang.Class对象
5.初始化某个类的子类
6.直接使用java.exe命令来运行某个主类
(二) 类加载器
概述: 类加载器是负责加载类的对象,将class文件加载到内存中,并为之生成对应的java.lang.Class对象.
2.1 类加载器的分类
1. Bootstrap ClassLoader 引导类加载器,通常表示为null
也被称为根类加载器,负责jre\lib目录下核心类的加载,比如System,String等.
2. Extension ClassLoader 扩展类加载器
负责JRE的扩展目录中jar包的加载,在JDK中JRE的lib目录下ext目录.
3. Application ClassLoader 系统类加载器
负责在JVM启动时加载来自java命令的class文件,以及classpath环境变量所指定的jar包和类路径.
4. 自定义类加载器
开发人员可以通过继承java.lang.ClassLoader类的方式实现自己的类加载器,以满足一些特殊的需求.
类加载器之间的关系是子父类, 但是不是继承关系
-Bootstrap ClassLoader
-Extension ClassLoader
-Application ClassLoader
import sun.net.spi.nameservice.dns.DNSNameService; public class ClassLoaderTest { public static void main(String[] args) { System.out.println(System.class.getClassLoader());// null System.out.println(String.class.getClassLoader());// null System.out.println(DNSNameService.class.getClassLoader());// sun.misc.Launcher$ExtClassLoader@677327b6 System.out.println(ClassLoaderTest.class.getClassLoader());// sun.misc.Launcher$AppClassLoader@18b4aac2 System.out.println("------------------------------"); ClassLoader cl = ClassLoaderTest.class.getClassLoader(); while(cl != null){ System.out.println(cl); cl = cl.getParent(); } } }
2.2 双亲委派机制
1. 双亲委派机制是指当一个类加载器收到一个类加载请求时,该类加载器首先会把请求委派给父类加载器.每个类加载器都是如此,只有在父类加载器在自己的搜索范围内找不到指定类时,子类加载器才会尝试自己去加载.
2. 双亲委派模型工作过程:
1)当Application ClassLoader 收到一个类加载请求时,他首先不会自己去尝试加载这个类,而是将这个请求委派给父类加载器Extension ClassLoader去完成.
2)当Extension ClassLoader收到一个类加载请求时,他首先也不会自己去尝试加载这个类,而是将请求委派给父类加载器Bootstrap ClassLoader去完成.
3)如果Bootstrap ClassLoader加载失败,就会让Extension ClassLoader尝试加载.
4)如果Extension ClassLoader也加载失败,就会使用Application ClassLoader加载.
5)如果Application ClassLoader也加载失败,就会使用自定义加载器去尝试加载.
6)如果均加载失败,就会抛出ClassNotFoundException异常.
3.例子:
当一个Hello.class这样的文件要被加载时.不考虑我们自定义类加载器,首先会在AppClassLoader中检查是否加载过,如果有那就无需再加载了.如果没有,那么会拿到父加载器,然后调用父加载器的loadClass方法.父类中同理会先检查自己是否已经加载过,如果没有再往上.注意这个过程,直到到达Bootstrap classLoader之前,都是没有哪个加载器自己选择加载的.如果父加载器无法加载,会下沉到子加载器去加载,一直到最底层,如果没有任何加载器能加载,就会抛出ClassNotFoundException.
2.3 ClassLoader类
1. ClassLoader 叫做类加载器.虚拟机设计团队把类加载阶段中的“通过一个类的全限定名来获取描述此类的二进制字节流” 也就是.class字节码文件,这个动作放到java虚拟机外部去实现,以便让应用程序自己决定去如何获取所需要的类,实现这个动作的代模块称之为“类加载器”.把【.class】文件加载到jvm虚拟机当中,转换为一个Class的对象【类的字节码对象 类的类对象】
2.ClassLoader的方法:
ClassLoader getParent()
返回父类加载器进行委派
import sun.net.spi.nameservice.dns.DNSNameService; public class ClassLoaderTest { public static void main(String[] args) { System.out.println(System.class.getClassLoader());// null System.out.println(String.class.getClassLoader());// null System.out.println(DNSNameService.class.getClassLoader());// sun.misc.Launcher$ExtClassLoader@677327b6 System.out.println(ClassLoaderTest.class.getClassLoader());// sun.misc.Launcher$AppClassLoader@18b4aac2 System.out.println("------------------------------"); ClassLoader cl = ClassLoaderTest.class.getClassLoader(); while(cl != null){ System.out.println(cl); cl = cl.getParent(); } } }
(三) 反射应用
3.1 反射机制的概述
反射是指在运行时去获取一个类的变量和方法信息.然后通过获取到的信息来创建对象,调用方法的一种机制.由于这种动态性,可以极大的增强程序的灵活性,程序不用在编译期就完成确定,在运行期仍然可以扩展.
举例说明 :
1. 例如项目工程中, 有两个类,Student, Teacher, 需要使用这两个类
2. 因为要使用上述两类, 这个两个类对应的.class字节码文件就会被类加载器从磁盘路径上加载进入到内存中
3. 一个类型一旦进入到内存,证明需要使用,证明代码需要运行, 这种状态就是动态的效果
4. 类加载器为这个正在运行的.class字节码文件, 创建出一个对应的Class对象, 对象中包含了class文件中所有代码内容(可以包含类中所有成员变量, 构造方法,方法...)
5. 使用Class对象, 获取出类型中所有的需要的成员, 这种使用方式称为反射
3.2 获取Class类对象三种方式
1.Class类: Class类型的实例表示正在运行的java应用程序的类或者接口.
2.Class类的对象: 想获取和操作类中的内容,首先要获取类的字节码对象(Class类对象),每一个正在运行的类,都有对应的字节码对象,获取了类的字节码对象,就可以使用这个对象的所有方法,这些方法都定义在Class类型中.
3.三种获取Class类对象的方式:
1)类名.class属性
2)对象名.getClass()方法
3)Class.forName(全类名)方法
全类名 : com.ujiuye.demos.Demo01 包名 + 类名
public class Demo01_CLass对象获取三种方式 { public static void main(String[] args) throws ClassNotFoundException { String s = "abc"; // 1. 直接使用类型名称.class, 获取这个类型的字节码文件对象 Class<Person> c = Person.class; System.out.println(c); // 2. object父类中getClass(): 获取这个类型的字节码文件对象 Person p = new Person(); Class c1 = p.getClass(); System.out.println(c1); // 3. Class类中静态方法功能内容: Class.forName(提供带有完整包名的类名), // 获取这个类的字节码文件对象 Class c2 = Class.forName("com.ujiuye.reflect.Person"); System.out.println(c2); // 注意: 一个类型的.class字节码文件加载进入到内存中之后, 类加载器会为这个字节码文件自动生成 // 一个字节码文件对象, 对象中包含有文件的一切内容和功能, 这个对象只有一个, 存在于堆内存中 // 上述的三个方法都是获取这个对象的手段 System.out.println(c == c1);// true System.out.println(c1 == c2);// true } }
3.3反射获取构造方法并使用
1. Class类获取构造方法对象:
方法分类:
Constructor<?>[] getConstructors()
返回所有public公共构造方法对象的数组
Constructor<?>[] getDeclaredConstructors()
返回所有构造方法对象的数组
Constructor getConstructor(Class<?>... parameterTypes)
返回单个公共构造方法对象
Constructor getDeclaredConstructor(Class<?>...parameterTypes)
返回单个构造方法对象
2. 注意:
getConstructor(Class<?>... parameterTypes)
getDeclaredConstructor(Class<?>...parameterTypes)
两方法的参数列表为可变参数,可变参数即参数的个数可以是任意个,0个,1个或者多个均可,任意的数据类型都有对应Class对象, 连基本数据数据类型也不例外 : int.class
3. Constructor类型:
1)Constructor类表达的含义就是一个类当中的构造方法,一个对象就表达了一个构造方法
2)构造方法对象应该具有的功能: 获取构造方法各种信息(构造方法修饰符、构造方法名称、构造方法的参数列表、构造方法的注解),最基本的一个功能就是,创建对象.
4. Constructor类用于创建对象的方法:
T newInstance(Object...initargs) 根据指定的构造方法创建对象,参数为所运行构造方法需要的实际参数.
public class Person { int i = 10; public Person(){} public Person(String name){ System.out.println(name); } Person(String name, int age){ System.out.println(name + "--" + age); } private Person(int age){ System.out.println(age); } }
import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; public class Demo02_ReflectConstructor { public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { // 1. 获取到Person类型的字节码文件对象 Class c = Class.forName("com.ujiuye.reflect.Person"); // 2. 获取到Person中的所有的公共修饰的构造方法 Constructor[] conArr = c.getConstructors(); for(Constructor con : conArr){ System.out.println(con); } // 3.Constructor<?>[] getDeclaredConstructors():返回所有构造方法对象的数组 Constructor[] conArrAll = c.getDeclaredConstructors(); for(Constructor con : conArrAll){ System.out.println(con + "-----"); } // 4.Constructor getConstructor(Class<?>... parameterTypes)回单个公共构造方法对象 // 想要获取到公共修饰的, 空参数构造方法 Constructor con1 = c.getConstructor(); System.out.println(con1); // 5.Constructor getDeclaredConstructor(Class<?>...parameterTypes)返回单个构造方法对象 Constructor con2 = c.getDeclaredConstructor(int.class); System.out.println(con2); // 6. Constructor构造器类: 表示一个构造方法,构造方法可以运行,运行效果就是创建出一个指定类型对象 // Constructor类中有一个方法功能: newInstance(Object...initargs) Person p = (Person)con1.newInstance(); System.out.println(p.i); // 7. 获取到默认修饰的构造方法 Constructor con3 = c.getDeclaredConstructor(String.class, int.class); Person p3 = (Person)con3.newInstance("张三",19); System.out.println(p3.i); } }