1 、反射(经过镜子看到类的结构)
- 反射能够在程序运转期间凭借
Refection
Api取得任何类的内部信息(比方成员变量,结构器,成员办法等),而且能操作目标的特点和办法。反射在规划模式和框架底层都会用到 - 加载完类之后,在堆中产生了一个
Class
类型的一个目标,这个目标包括了类的完好结构信息。经过这个目标得到类的结构。
1.1 反射原理图
1.2 反射快速入门
// classfullpath=com.lyq.Cat
// method=cry
Properties properties = new Properties();
properties.load(new FileInputStream("src\re.properties"));
String classfullpath = properties.get("classfullpath").toString();
String methodName = properties.get("method").toString();
System.out.println(classfullpath);
System.out.println(methodName);
// 反射快速入门
// 1. 加载类
Class<?> aClass = Class.forName(classfullpath);
// 2. 得到 cls 得到加载类 com.lyq.Cat 的目标实例
Object cat = aClass.newInstance();
// 3. 经过 cls 得到 Cat 里边的 hi 目标
// 即:在反射中,万物皆目标
Method method = aClass.getMethod(methodName);
// 4. 经过 method 来唤醒 cat 的对应办法,
method.invoke(cat); // 传统是 目标.办法, 反射是: 办法.invoke(目标)
1.3 反射相关类
import java.lang.Class; // 类目标
import java.lang.reflect.Method; // 类的办法
import java.lang.reflect.Field; // 类的成员变量
import java.lang.reflect.Constructor; // 代表类的结构办法
Properties properties = new Properties();
properties.load(new FileInputStream("src\re.properties"));
String classfullpath = properties.getProperty("classfullpath");
String methodName = properties.getProperty("method");
Class<?> aClass = Class.forName(classfullpath);
Object o = aClass.getDeclaredConstructor().newInstance();
Method method = aClass.getMethod(methodName);
method.invoke(o);
// getField 不能得到私有特点
Field ageField = aClass.getField("age");
System.out.println(ageField.get(o));
// String.class 代表形参类型
Constructor<?> constructor = aClass.getConstructor(String.class);
System.out.println(constructor);
1.4 反射长处和缺陷
- 长处:能够动态创立和运用目标,灵敏,没有反射机制,框架技能短少底层支撑
- 缺陷:运用反射根本是解说履行,对履行速度又影响
1.5 反射优化
- Method 和 Field 和 Constructor 都有
setAccssible()
办法 默以为false,查看拜访安全,能够设置为true,撤销查看
1.6 Class 类
- Class类也是类,继承 Object
- Class 不是 new 出来的类,而是类加载器经过 loadClass()办法创立
public Class<?> loadClass(String name) throws ClassNotFoundException { return loadClass(name, false); }
- 类只会被加载一次
- Class 目标存放在
堆
中 - 类的
二进制字节码数据存放在办法区
中
1.6.1 Class 类常用办法
String s = "com.lyq.Cat";
Class<?> cls = Class.forName(s);
System.out.println(cls.getName()); // 获取全类名 com.lyq.Cat
System.out.println(cls.getClass()); // 获取类名 class java.lang.Class
System.out.println(cls.getPackage()); // 获取包名 package com.lyq
Cat o = (Cat) cls.getConstructor().newInstance();
System.out.println(o); // Cat{name='小黄', age=15}
// 得到 Cat 类的 age 特点
Field age = cls.getField("age");
System.out.println(age.get(o)); // 15
// 经过反射给特点赋值
age.set(o, 123123123);
System.out.println(age.get(o));
// 获取 Cat 目标的一切特点
Field[] fields = cls.getFields();
for (Field o : fields) {
System.out.println(o);
}
1.7 获取 Class 目标的六种 方式
- Class.forName() :
多用于配置文件
- 类.class :
多用于参数传递
。比方经过反射得到类目标 - 目标.getClass()
- 类加载器 getClassLoader() 得到 Class
- 包装类.TYPE
// 1. Class.forName
Class<?> aClass = Class.forName("com.lyq.Cat");
System.out.println(aClass);
// 2. 类名.class
System.out.println(Cat.class);
// 3. getClass()
Cat cat = new Cat();
System.out.println(cat.getClass());
// 4. 类加载器 Classloader
ClassLoader classLoader = cat.getClass().getClassLoader();
Class<?> aClass1 = classLoader.loadClass("com.lyq.Cat");
System.out.println(aClass1);
// 5. 根本数据类型
Class<Integer> integerClass = int.class;
Class<Character> characterClass = char.class;
// 6. 包装类
Class<Integer> type = Integer.TYPE;
1.8 那些类型有Class目标
- 外部类、成员内部类、静态内部类、局部内部类、匿名内部类
- 接口
- 枚举
- 数组
- 注解
- 根本数据类型
- void
Class<String> stringClass = String.class; // 外部类
Class<Serializable> serializableClass = Serializable.class; // 接口
Class<Integer[]> aClass = Integer[].class; // 数组
Class<Enum> enumClass = Enum.class; // enum 枚举
System.out.println(Deprecated.class); // 注解
Class<Long> longClass = long.class; // 根本数据类型
Class<Void> voidClass = void.class; // void
1.9 静态加载和动态加载
- 静态加载:不运用反射,
在编译期间就会加载的类
,假如没有则报错,依赖性强 - 动态加载:运用反射,依赖性不强,可是时间久
1.10 类加载进程
1.10.1 类加载进程详解
- 加载阶段:jvm在该阶段的首要意图是将字节码从不同的数据源(class、jar包等)转化为
二进制字节省加载到内存中
,而且生成代表该类的java.lang.Class
目标 - 连接阶段
-
验证:
- 意图是为了确保Class文件的字节省中包括的信息符号JVM机的要求,而且不会危害到JVM机
- 包括:文件格式验证(是否以魔数 oxcafebabe最初)、元数据验证、字节码严重和符号引证验证
- 能够考虑运用
-Xverify:none
参数关闭大部分的验证办法,缩短虚拟机类加载时间
-
准备:
- 在该阶段会对
静态变量
分配内存并默许初始化(0,null,false等)。这些变量所运用的内存都将在办法区中进行分配
- 在该阶段会对
-
解析:
- 虚拟机将常量池内的符号引证替换成直接引证的进程
-
初始化:
- 到该阶段,才真正开始履行类中界说的 Java 程序代码,此阶段是履行
<clinit>()
办法的进程 -
<clinit>()
办法是由编译器句子在源文件中呈现的次序,顺次自动搜集类中的一切静态变量
的赋值动作和静态代码块
中的句子,并进行合并 - 虚拟时机保证一个类的
<clinit>()
办法在多线程环境下被正确加锁、同步,假如多个线程一起去初始化一个类,那么只有一个类会初始化成功,其他将被堵塞,直到活动线程履行<clinit>
()办法结束
- 到该阶段,才真正开始履行类中界说的 Java 程序代码,此阶段是履行
-
1.11 经过反射获取类的结构信息
-
java.lang.Class 类
- getName :获取全类名
- getSimpleName :获取简略类名
- getFields :获取一切public修饰的特点,包括本类以及父类
- getMethods :获取一切public修饰的办法,包括本类以及父类
- getConstructors : 获取一切public修饰的结构器
- getDeclaredConstructors :获取本类中一切结构器
- getPackage :以Package方式回来包信息
- getSuperClass :以Class方式回来父类信息
- getlnterfaces :以Class方式回来接口信息
- getAnnotations :以Annotation方式回来注解信息
-
java.lang.reflect.Method 类
- getModifiers:以 int 方式回来修饰符
- 默许修饰符是 0
- public 是 1
- private 是 2
- protected 是 4
- static 是 8
- final 是 16
- getReturnType:以 class 方式获取回来类型
- getName :回来办法名
- getParameterTypes:以 Class[]回来参数类型数组
- getModifiers:以 int 方式回来修饰符
-
java.lang.reflect.Method 类
- getModifiers:以 int 方式回来修饰符
- 默许修饰符是 0
- public 是 1
- private 是 2
- protected 是 4
- static 是 8
- final 是 16
- getType:以 Class 方式回来类型
- getName : 回来特点名
- getModifiers:以 int 方式回来修饰符
1.12 反射爆炸创立实例
-
经过调用类中的 public 修饰的无参结构器
-
调用类中的指定结构器
-
Class 类相关办法
- newInstance :调用类中的无参结构器,获取对应类的目标
- getCOnstructor(Class … clazz) :依据参数列表,获取对应的 public 结构器目标
- getDecalaredConstructor(Class … class) :依据参数列表,获取全部的结构器目标
-
Constructor 类相关办法
- setAccessible :爆炸
- newInstance(Object…obj) :调用结构器
1.12.1 经过反射拜访结构器
String path = "com.lyq.reflection_.User";
Class<?> cls = Class.forName(path);
// 拜访公有结构器,而且调用
Object publicConstructor = cls.getConstructor().newInstance(); // User{age=10, name='lyq'}
Constructor<?> declaredConstructor = cls.getDeclaredConstructor(int.class, String.class);
// 经过 setAccessible 能够拜访私有结构器,而且调用私有结构器
declaredConstructor.setAccessible(true);
Object lyq = declaredConstructor.newInstance(1, "lyq222");
System.out.println(lyq); // User{age=1, name='lyq222'}
1.12.2 经过反射操作特点
Class<?> cls = Class.forName("com.lyq.reflection_.Student");
Constructor<?> constructor = cls.getConstructor();
Object o = constructor.newInstance();
Field age = cls.getField("age");
age.set(o, 20202020);
System.out.println(age.get(o)); // 反射设置特点值 20202020
Field name = cls.getDeclaredField("name");
// 对 name 进行暴力破解
name.setAccessible(true);
name.set(o, "zzz");
System.out.println(o); // Student{age=20202020, name='zzz'}
1.12.3 经过反射操作办法
Class<?> cls = Class.forName("com.lyq.reflection_.Boss");
Object o = cls.getConstructor().newInstance();
// 调用公有办法
Method method = cls.getMethod("hi", String.class);
method.invoke(o, "dasda"); // hi dasda
// 调用私有办法
Method say = cls.getDeclaredMethod("say", int.class, String.class, char.class);
// 暴力破解
say.setAccessible(true);
Object invoke = say.invoke(o, 100, "ccccc", 'k');
System.out.println(invoke); // 100 ccccc k
事例:运用反射创立文件
Class<File> fileClass = File.class;
Constructor<File> declaredConstructor = fileClass.getDeclaredConstructor(String.class);
File file = declaredConstructor.newInstance("d:\aa.txt");
file.createNewFile();
System.out.println(file);