-
2020-12-21 06:16:11
今天在面试的时候遇到一个非常nice的面试官,当时问到了如何通过反射来获取私有属性,
虽然本人之前也有做过,不过今天面试官一问顿时一脸懵逼,于是打算写这篇博客记录下来。先来看下我们的javaBean
public class Test {
private String name;
private int age;
private Test(int age){
this.age = age;
}
private void speak(String name){
System.out.println("我的名字是:"+name);
}
public Test(String name) {
this.name = name;
}
}
首先,我们要了解三个反射包中的类:
Constructor:代表类的单个构造方法,通过Constructor我们可执行一个类的某个构造方法(有参或者无参)来创建对象时。
Method:代表类中的单个方法,可以用于执行类的某个普通方法,有参或无参,并可以接收返回值。
Field:代表类中的单个属性,用于set或get属性
AccessibleObject:以上三个类的父类,提供了构造方法,普通方法,和属性的访问控制的能力。
使用Class类中的方法可以获得该类中的所有Constructor对象,Method对象,和Field对象。
但是任然无法访问私有化的构造方法,普通方法,和私有属性,此时我们可以使用他们继承父类(AccessibleObject)中的
setAccessible()方法,来设置或取消访问检查,以达到访问私有对象的目的。
Step1:获取私有属性
public static void main(String[] args) throws NoSuchFieldException {
// TODO Auto-generated method stub
/*
* 使用反射来创建构造方法私有化的对象
* */
//1:获取类的无参构造方法
Test test = new Test("张三");
Method[] methods = Test.class.getMethods();
Field[] fields = Test.class.getDeclaredFields();
for (int i = 0; i < fields.length; i++) {
fields[i].setAccessible(true);
try {
System.out.println(fields[i].get(test));
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(fields[i].getName());
}
}
然后,我们再来看一下输出的:
张三
name
0
age
Step2:获取私有方法
Method[] methods2 = Test.class.getDeclaredMethods();
for (int i = 0; i < methods2.length; i++) {
methods2[i].setAccessible(true);
System.out.println(methods2[i].getName());
}
然后,我们再来看一下输出的:
speak
Step3:调用私有方法
Test test = new Test("张三");
Method[] methods = Test.class.getDeclaredMethods();
for (int i = 0; i < methods.length; i++) {
methods[i].setAccessible(true);
try {
methods[i].invoke(test,"成功调用");
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InvocationTargetException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(methods[i].getName());
}
这样,我们就获得了私有属性的值啦~~当然,凡事有利就有弊,然后我们再来说一下java反射的优缺点;
优点:
1:能够运行时动态获取类的实例,大大提高了系统的灵活性和扩展性;
2:与java动态编译相结合,可以实现无比强大的功能。
缺点:
1:使用反射的性能较低;
2:使用反射来说相对不安全;
3:破坏了类的封装性,可以通过反射来获取这个类的属性,和私有方法。
更多相关内容 -
反射获取构造方法/成员变量/成员方法setAccessible基本使用serviceLoader
2022-03-15 16:24:46类加载 类加载概述 概念: 当程序要使用某个类时,如果该类未被加载到内存中,则系统会通过类的加载、类的...验证阶段:用于检验被加载的类是否有正确的内部结构,并与其他类协调一致 准备阶段:负责为类的类变量分配文章目录
类加载
类加载概述
概念:
- 当程序要使用某个类时,如果该类未被加载到内存(JVM内存)中,则系统会通过类的加载、类的连接、类的初始化三个步骤对类进行初始化。
- JVM会将类加载、连接、初始化连续完成,这三个步骤被称为类加载或类初始化。
类的加载
- 指将.class文件读入内存,并为之创建一个java.lang.Class对象类加载器的作用
- 任何类被使用的时候,系统都会为之创建一个java.lang.Class对象
类的连接
- 验证阶段:用于检验被加载的类是否有正确的内部结构,并与其他类协调一致
- 准备阶段:负责为类的类变量分配内存,并设置默认初始化值
- 解析阶段:将类的二进制数据中的符号引用替换为直接引用
类的初始化
- 主要对类变量进行初始化
类的初始化步骤:
- 假如类还未被加载和连接,则程序先加载并连接该类
- 假如该类的直接父类还未被初始化,则先初始化被直接父类
- 假如类中有初始化语句,则系统依次执行这些初始化语句
注意:
在执行第二个步骤的时候,系统对直接父类的初始化步骤也遵循初始化步骤1-3
一个类被载入java虚拟机的时候,同一个类就不会再次被载入了。类的初始化时机: 强调首次
- 创建类的实例
- 调用类的类方法(静态方法)
- 访问类或者接口的类变量(静态变量),或者为该类变量赋值
- 使用反射方式来强制创建某个类或接口对应的java.lang.Class对象
- 初始化某个类的子类(初始化子类首先就会初始化父类)
- 直接使用java.exe命令来运行某个主类
JVM的类加载机制
JVM的类加载机制有三种:全盘负责、父类委托、缓存机制。
- 全盘负责:当一个类加载器负责加载某个Class时,该Class所依赖的和引用的其他Class也由该类加载器负责载入,除非显示使用另外一个类加载器载入;
- 父类委托:当一个类加载器负责加载某个Class时,先让父类加载器试图加载该Class,只有在父类加载器无法加载该类时才尝试从自己的类路径中加载该类;
- 缓存机制:保证所有被加载的Class都会被缓存,当程序需要使用某个Class对象时,类加载器先从缓存区中搜索该Class,只有当缓存区中不存在该Class对象时,系统才会读取该类对应的二进制数据,并将其转换成Class对象,存储在缓存区。
ClassLoader: 负责加载类的对象
java运行时具有以下内置类加载器:类名 说明 Bootstrap class loader 它是虚拟机的内置类加载器,通常表示null,并且没有父null
也就是这个类是类加载器的祖宗Platform class loader 平台类加载器可以看到所有平台类,平台类包括由平台类加载器或其祖先定义的java SE平台API,其实现类和JDK特定的运行时类 System class loader
AppClassLoader它也被称为应用程序类加载器,与平台类加载器不同。系统类加载器通常用于定义应用程序类路径、模块路径和JDK特定工具上的类 类加载器的继承关系:System的父加载器为Platform(java9)Ext(java8),Platform的父加载器为Bootstrap。
ClassLoader中的两个方法:
方法名 说明 static ClassLoader getSystemClassLoader() 返回用于委派的系统类加载器
ClassLoader.getSystemClassLoader()ClassLoader getParent() 返回父类加载器进行委派 案例:
package itiheima315.test1; public class ClassLoaderDemo { public static void main(String[] args) { // 系统类加载器 查看当前类的类加载器 ClassLoader c = ClassLoader.getSystemClassLoader(); System.out.println(c);//AppClassLoader(应用程序类加载器) // 父类加载器 ClassLoader c2 = c.getParent(); System.out.println(c2);//ExtClassLoader(扩展类加载器) 视频中是 PlatformClassLoader // java8 的ExtClassLoader = java9 的 PlatformClassLoader ClassLoader c3 = c2.getParent(); System.out.println(c3);// null } }
反射
概念:
- java反射机制:运行时去获取一个类的变量和方法信息,通过获取到的信息创建对象,调用方法的一种机制;
- 动态性,可以极大的增强程序的灵活性,程序不用在编译期就完成确定,在运行期仍然可以扩展;
理解xhj:
- 自己定义类要想使用–》类加载器加载对应的.class文件–》每一个.class文件都会包含成员变量、构造方法、成员方法…信息
- Class类就是所有.class文件对应的类型、躯体。
- 不再通过自己定义类的去使用成员变量、构造方法、成员方法,而是用Class类去使用成员变量、构造方法、成员方法,这就是反射。
- 反射就是把java类中的各种成分映射成相应的java类。
获取Class类的对象 Class.forName(常用)
原因: 通过反射去使用一个类。首先获取该类的 字节码文件对象=类型为Class类型的对象。
获取Class类型对象的方法:方法 说明 使用类的class属性来获取该类对应的Class对象 Student.class将会返回Student类对应的Class对象 调用对象的getClass()方法,返回该对象所属类对应的Class对象 该方法是Object类中的方法,所有的Java对象都可以调用该方法 使用Class类中的静态方法forName(String className) 该方法需要出入 字符串参数 = 某个类的全路径=完整包名的路径 案例:
package itiheima315.test2; public class ReflectDemo { public static void main(String[] args) { 1 使用《类的class属性》来获取该类对应的Class对象 Class<Student> c1 = Student.class; System.out.println(c1); // 输出内容是:class itiheima315.test2.Student // 一个类在内存中只有一个字节码文件 Class<Student> c2 = Student.class; System.out.println(c1 == c2); // 输出:true System.out.println("------"); 2 调用《对象的getClass()方法》 Student s = new Student(); Class<? extends Student> c3 = s.getClass(); System.out.println(c1 == c3); 3 使用《Class类中的静态方法forName(String className)》 Class<?> c4 = null; try { c4 = Class.forName("itiheima315.test2.Student"); } catch (ClassNotFoundException e) { e.printStackTrace(); } System.out.println(c1 == c4); } }
反射获取构造方法并使用 Constructor
Class类
- 在java.lang包下,使用需要导包;Class Class< T >
- public final class Class< T > extends Object implements …是最终类,说明不能被继承
- Class类的实例表示正在运行的java应用程序的类和接口
- 通过Class对象获取Constructor对象的数据
所属类 方法名 说明 Class类 Constructor< ? >[] getConstructors() 返回所有公共构造方法对象的数组 Class类 Constructor<?>[] getDeclaredConstructors() 返回所有构造方法对象的数组 Class类 Constructor< T> getConstructor(Class<?>…parameterType) 返回单个公共构造方法对象
要获取的构造方法的参数的个数和数据类型对应的字节码文件对象
使用的是:String类型,则用String.class;int类型,则用的是int.classClass类 Constructor< T> getDeclaredConstructor(Class<?>…parameterType) 返回单个构造方法对象
要获取的构造方法的参数的个数和数据类型对应的字节码文件对象Constructor类
- 在java.lang.reflect包下,使用需要导包
- public final class Constructor< T> extends Executable 是最终类
- 提供了一个类的单个构造函数的信息和访问权限
- 通过Constructor类中的newInstance方法创建对象:
方法名 说明 T newInstance(Object…initargs) 使用由此Constructor对象表示的构造函数创建对象,
使用指定的初始化参数创建和初始化构造函数的声明类的新实例理解xhj:
- 在反射中,实际上是将类中的成员变量、构造方法、成员方法,看成一个个对象;
- 通过反射创建类的对象:
1 通过Class.forName方法得到 类的字节码文件对象 Class<?> c = Class.forName("类的路径") 抛出异常 ClassNotFoundException 2 通过字节码文件对象的getConstructor方法得到单个构造函数 Constructor<?> con = c.getConstructor(); 抛出异常 NoSuchMethodException 3 通过Constructor对象的newInstance方法创建对象 Object obj = con.newInstance(); 抛出异常 IllegalAccessException, InvocationTargetException, InstantiationException
案例:
package itiheima315.test2; public class ReflectDemo2 { public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { // 获取Class对象,也就是类的字节码对象 Class<?> c = Class.forName("itiheima315.test2.Student"); // 得到Student类的字节码文件对象 // 要想获取构造方法,要在Class类中找方法获取构造方法。 // 即getConstructors、getDeclaredConstructors、getConstructor、getDeclaredConstructor Constructor< ? >[] getConstructors() :返回一个包含Constructor对象的数组 // Constructor<?>[] arrayC = c.getConstructors(); // for(Constructor con:arrayC){ // System.out.println(con); // } // 结果: // public itiheima315.test2.Student(java.lang.String,int,java.lang.String) //public itiheima315.test2.Student() Constructor<?>[] getDeclaredConstructors() :返回反映该Class对象的类声明的所有构造函数的Constructor对象的数组 // Constructor<?>[] conS = c.getDeclaredConstructors(); // for (Constructor con : conS) { // System.out.println(con); // } // 结果 /* public itiheima315.test2.Student(java.lang.String,int,java.lang.String) 公共构造方法 itiheima315.test2.Student(java.lang.String,int) 默认构造方法 private itiheima315.test2.Student(java.lang.String) 私有构造方法 public itiheima315.test2.Student()*/ 公共无参构造方法 Constructor< T> getConstructor(Class<?>.....parameterType):返回一个Constructor对象,该对象反映该Class对象表示的类的指定公共构造函数| Constructor<?> c0 = c.getConstructor();//拿public的无参构造方法 // System.out.println(c0); // 使用Constructor对象来实现通过类创建对象。 Object obj = c0.newInstance(); System.out.println(obj); // 输出Student{name='null', age=0, address='null'} // Constructor< T> getDeclaredConstructor(Class<?>...parameterType)|返回一个Constructor对象,该对象反映由此Class对象表示的类或接口的指定构造函数| } }
Student类
package itiheima315.test2; public class Student { private String name;//私有成员变量 int age;//默认 public String address;//公共 // 构造方法 // 公共 public Student(){} // 私有 private Student(String name){ this.name = name; } // 默认 Student(String name,int age){ this.name = name; this.age = age; } // 公共 public Student(String name,int age,String address){ this.name = name; this.age = age; this.address = address; } // 成员方法 private void function(){ System.out.println("function"); } public void method1(){ System.out.println("method"); } public void method2(String s){ System.out.println("method:" + s); } public String method3(String s,int i){ return s + ", " + i; } @Override public String toString() { return "Student{" + "name='" + name + '\'' + ", age=" + age + ", address='" + address + '\'' + '}'; } }
案例:反射
案例1
需求:
通过反射实现以下操作:
Student s = new Student(“林俊杰”,30,“西安”); System.out.println(s);
思路2022/5/19 1 获得class对象 类.class属性 2 获得constructor对象 class对象.getConstructor(String.class,int.class,String.class); 3 使用newInstance方法创建对象 constructor对象.newInstance("参数1","参数2","参数3");
基本数据类型可以通过.class得到对应的Class类型
package itiheima315.test2; public class ReflectTest1 { public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { /* Student s = new Student("林俊杰",30,"西安"); System.out.println(s);*/ Class<?> c = Class.forName("itiheima315.test2.Student"); // 获取Class对象 Constructor<?> con = c.getDeclaredConstructor(String.class,int.class,String.class); Object obj = con.newInstance("林俊杰", 30, "西安"); System.out.println(obj); } }
案例2
需求:
通过反射实现以下操作:
Student s = new Student(“林俊杰”); System.out.println(s);public void setAccessible(boolean flag):值为true,取消访问检查 被称为暴力反射
作用:
当通过getDeclaredConstructor方法获得私有构造方法 并 创建对象的时候,会报错,IllegalAccessException
解决:对通过getDeclaredConstructor方法获得的私有构造方法使用setAccessible方法可以取消访问检查,即可实现利用私有构造方法创建对象。package itiheima315.test2; public class ReflectTest2 { public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { /*Student s = new Student("林俊杰"); System.out.println(s);*/ Class<?> c = Class.forName("itiheima315.test2.Student"); Constructor<?> con = c.getDeclaredConstructor(String.class); // 暴力反射 // public void setAccessible(boolean flag):值为true,取消访问检查 con.setAccessible(true); Object obj = con.newInstance("林俊杰"); System.out.println(obj) //获取到的私有构造方法不能创建对象 会报错 IllegalAccessException //加了setAccessible方法 可以实现私有方法创建对象 // 输出:Student{name='林俊杰', age=0, address='null'} } }
反射获取成员变量并使用 Field
获取成员变量的方法
方法名 说明 Field[] getFields() 返回类或接口的所有可访问的公共字段组成的数组 Field[] getDeclaredFields() 返回类或接口声明的所有字段组成的数组 Field getField(String name) 返回类或接口的指定公共字段 Field getDeclaredField(String name) 返回类或接口的指定字段 Field类
- Field提供有关类或接口的单个字段的信息和动态访问
- 所给成员变量赋值的方法:
方法名 说明 void set(Object obj,Object value) 将指定的对象参数,设置为由Field对象表示的字段
成员变量名.set(对象,“具体值”) # 给对象的成员变量赋值为具体值反射获取成员变量具体的代码过程:
1 获取Class对象 Class<?> c = Class.forName("类的路径"); 2 获取Class对象的无参构造方法 Constructor<?> con = c.getConstructor(); 3 获取Class对象的成员变量 Field fName = c.getField("成员变量名"); 4 通过无参构造方法创建对象 Object obj = con.newInstance(); 5 给对象的成员变量赋值 fName.set(obj,"设置的值");
需求:反射获取成员变量并使用
package itiheima315.test2; public class ReflectDemo3 { public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException , IllegalAccessException, InvocationTargetException, InstantiationException { // 获取类的Class对象 Class<?> c = Class.forName("itiheima315.test2.Student"); // Field[] fields = c.getFields();//获取公共的成员变量 Field[] fields = c.getDeclaredFields();//获取所有成员变量组成的数组 for(Field f:fields){ System.out.println(f); } System.out.println("-------"); // 获取成员变量 Field addressField = c.getField("address"); //获取无参构造方法创建对象 Constructor<?> con = c.getConstructor(); Object obj = con.newInstance(); addressField.set(obj,"西安"); System.out.println(obj); } }
练习:
需求:
通过反射实现:
Student s = new Student();
s.name = “汪苏泷”;
s.age = 30;
s.address = “阳泉”;
System.out.println(s);package itiheima315.test2; public class ReflectTest3 { public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchFieldException { // 通过反射实现 /* Student s = new Student(); s.name = "汪苏泷"; s.age = 30; s.address = "阳泉"; System.out.println(s);*/ // 获取Class对象 Class<?> c = Class.forName("itiheima315.test2.Student"); // 获取无参构造方法 Constructor<?> con = c.getConstructor(); // 创建对象 Object obj = con.newInstance(); // 获取成员变量 // Field fname = c.getField("name"); Field fname = c.getDeclaredField("name"); // 暴力反射 fname.setAccessible(true); Field fage = c.getDeclaredField("age"); fage.setAccessible(true); Field faddress = c.getField("address"); // 给对象的成员变量赋值 fname.set(obj, "汪苏泷"); fage.set(obj, 30); faddress.set(obj,"北京"); System.out.println(obj); } }
注意:
当想使用私有的成员变量的时候,需要进行如下操作:Field f = class类对象.getDeclaredField("成员变量名"); f.setAccessible(ture); // 取消访问检查
当想使用私有成员方法的时候,需要进行如下操作:
Method method= class类对象.getDeclaredMethod("成员方法名",参数的.class对象); method.setAccessible(ture); // 取消访问检查
反射获取成员方法并使用 Method
获取成员方法的方法:
方法名 说明 Method[] getMethods() 返回类或接口的所有公共方法
包括类或接口声明的对象以及从超类和超级接口继承的类Method[] getDeclaredMethods() 返回类或接口声明的所有方法
包括public、protected、default和private,不包括继承方法Method[] getMethod(String name,Class<?> … parametreTyoes) 返回类或接口的指定公共成员方法 Method[] getDeclaredMethod(String name,Class<?> … parametreTyoes) 返回类或接口的指定成员方法 Method方法
- 在类或接口上提供有关单一方法的信息和访问权限
- 实现对象调用方法
方法名 说明 Object invoke(Object obj,Object …args) 在具有指定参数的指定对象上调用此方法表示的基础方法
Object返回值类型;obj调用方法的对象;args方法需要的参数反射获取成员方法的具体代码实现:
1 获取Class对象 Class<?> c = Class.forName("类的路径"); 2 获取无参构造方法 Constructor con = c.getConstructor(); 3 无参构造方法创建对象 Object obj = con.newInstance(); 4 获取成员方法 Method m = c.getMethod("成员方法名"); 5 对象使用成员方法对象 m.invoke(obj,"成员方法所需参数");
代码:
package itiheima315.test2; public class ReflectDemo4 { public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { Class<?> c = Class.forName("itiheima315.test2.Student"); // Method[] methods = c.getMethods();//本类的 以及 继承的公共方法 // Method[] methods = c.getDeclaredMethods();// 本类所有的方法 // for(Method method:methods){ // System.out.println(method); // } // } Method m1 = c.getMethod("method1"); // 获取无参构造方法 Constructor<?> con = c.getConstructor(); Object obj = con.newInstance(); m1.invoke(obj); } }
练习
练习1
需求
2022/5/19 // Student s = new Student(); Class stuclass = Student.class // 创建类的对象 Constructor stucon = stuclass.getConstructor(); Object obj = stucon.newInstance(); // s.method1(); Method method1 = stuclass.getMethod("method1"); method1.invoke(obj); // s.method2("汪苏泷"); Method method2 = stuclass.getMethod("method2",String.class); method2.invoke(obj,"汪苏泷"); // String ss = s.method3("许嵩",32); Method method3 = stuclass.getMethod("method3",String.class,int.class); Object obj1 = method3.invoke(obj,"许嵩",32); // System.out.println(ss); String ss = (String)obj1; System.out.println(ss); //s.function(); 私有方法 Method function = stuclass.getDeclaredMethod("function"); function.setAccessible(true); function.invoke(obj);
代码:
package itiheima315.test2; public class ReflectTest4 { public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { // //使用反射实现如下操作: // Student s = new Student(); // s.method1(); // s.method2("汪苏泷"); // String ss = s.method3("许嵩",32); // System.out.println(ss); // s.function(); Class<?> c = Class.forName("itiheima315.test2.Student"); Constructor<?> con = c.getConstructor(); Object obj = con.newInstance(); // s.method1(); Method method1 = c.getDeclaredMethod("method1"); method1.invoke(obj ); // s.method2("汪苏泷"); Method method2 = c.getDeclaredMethod("method2", String.class); method2.invoke(obj,"汪苏泷"); //String ss = s.method3("许嵩",32); Method method3 = c.getDeclaredMethod("method3", String.class, int.class); Object ss = method3.invoke(obj, "许嵩", 30); // System.out.println(ss); String st = (String)ss; System.out.println(ss); // s.function(); Method function1 = c.getDeclaredMethod("function"); // 由于function是私有成员,所以需要暴力反射 function1.setAccessible(true); function1.invoke(obj); } }
Student类
package itiheima315.test2; public class Student { private String name;//私有成员变量 int age;//默认 public String address;//公共 // 构造方法 // 公共 public Student(){} // 私有 private Student(String name){ this.name = name; } // 默认 Student(String name,int age){ this.name = name; this.age = age; } // 公共 public Student(String name,int age,String address){ this.name = name; this.age = age; this.address = address; } // 成员方法 private void function(){ System.out.println("function"); } public void method1(){ System.out.println("method"); } public void method2(String s){ System.out.println("method:" + s); } public String method3(String s,int i){ return s + ", " + i; } @Override public String toString() { return "Student{" + "name='" + name + '\'' + ", age=" + age + ", address='" + address + '\'' + '}'; } }
练习2
有一个ArrayList< Integer>集合,在这个集合中添加一个字符串数据,如何实现?
反射可以完成一些正常情况下无法完成的事情
反射可以越过泛型检查的,获取到原始的方法所需要的参数类型
代码:package itiheima315.test2; public class ReflectTes5 { public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { ArrayList<Integer> array = new ArrayList<Integer>(); // array.add(10); // array.add(20); // 获取Class对象,使用对象.getClass方法实现。 Class<? extends ArrayList> c = array.getClass(); Method madd = c.getMethod("add",Object.class); madd.invoke(array, "pretty"); madd.invoke(array,"sunshine"); System.out.println(array); } }
练习3 : 通过配置文件运行类中的方法
代码:
package itiheima315.test3; public class ReflectDemo { public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { // Student s = new Student(); // s.study(); // // Teacher t = new Teacher(); // t.teach(); // 为了方便使用Student和Teacher两个类,可以使用配置文件完成 而不用每次修改main方法 // 加载数据 Properties prop = new Properties(); // 创建持久属性集 properties FileReader fr = new FileReader(".\\class.txt"); // 读取字符流 // char[] chs = new char[1024]; // int len; // while((len = fr.read(chs)) != -1){ // System.out.println(new String(chs,0,len)); // } // 输出结果是: // className=itiheima315.test3.Student //MethodName=study // 说明 内容已被读入 prop.load(fr); fr.close(); // 字符流关闭 // 数据文件加载成功 String className = prop.getProperty("className"); // 使用指定键索引属性 String methodName = prop.getProperty("MethodName"); // 使用不使用 类以及其成员方法 在class.txt文件中修改 // 通过反射来使用 Class<?> c = Class.forName(className);//得到的是itiheima315.test3.Student Constructor<?> con = c.getConstructor(); Object obj = con.newInstance(); Method m = c.getMethod(methodName); // System.out.println(m); m.invoke(obj); } }
案例涉及Properties方法
方法名 说明 String getProperty(String key) 使用此属性列表中指定的键搜索属性 void load(InputStream instream) 从输入字节流读取属性列表 (键值对) 注意
2022/5/19 properties内容在java25中。 模块化
概念
- 随着java语言的发展,逐渐成为一个“臃肿”的语言,无论是大系统还是小软件,JVM都需要加载整个JRE环境;
- java9实现了模块化,成功给java实现了瘦身,允许java程序可以根据需求选择要加载的模块;
- 整体的项目project,下面依次是:模块、包、类或者接口;
- 模块与模块之间是独立的,也可以作为访问权限的界定边界,可以通过模块的描述文件设置包是否被暴露处理、隐藏处理,对于隐藏的包即使它所包含的java类型使用了public修饰,别的模块仍旧不能访问这些类型。
模块基本使用
基本使用步骤:
- 创建模块 …包、类、定义方法
定义两个模块,myOne和MyTwo - 在模块的src目录下新建一个名为module-info.java的描述性文件,该文件专门定义模块名、访问权限、模块依赖等信息,描述性文件中使用模块导出和模块依赖进行配置并使用。
所要使用的和被使用的两个模块都创建module-info.java文件 - 模块中所有未导出的包都是模块私有的,他们不能在模块之外被访问
模块导出格式:exports 包名; - 一个模块要访问其他模块,必须明确指定依赖那些模块,未明确指定依赖的模块不能访问
模块依赖格式:requires 模块名;
注意:写模块名报错,需要按下Alt+enter,选择模块依赖。
代码:
MyOne模块package itiheima1; public class Student { public void study(){ System.out.println("你的自律,给你自信"); } } package itiheima2; public class Teacher { public void teach(){ System.out.println("全心付出只为金榜题名"); } } module MyOne { exports itiheima1; }
MyTwo模块:
package itiheima315; import itiheima1.Student; public class Test1 { public static void main(String[] args) { Student s = new Student(); s.study(); } } // module-info.java文件 module MyTwo { requires MyOne; }
模块服务的使用
概述:
- java6开始,提供了服务机制 = 允许服务提供者和服务使用者之间完成解耦 = 服务使用者只面向接口编程,不清楚服务提供者的实现类
- java9的模块化编程系统进一步简化了java的服务机制
- java9允许 = 》服务接口定义在模块中 =》使用uses语句声明该服务接口=》针对服务接口提供不同的服务实现类=》服务实现模块使用provides语句为服务接口指定实现类。 同样是服务使用者面向接口编程
模块服务的使用步骤
- 在模块a下创建包a,创建接口a定义抽象方法a
- 在包a下创建包b,创建接口a的两个实现类 类a和类b
- 模块a中的描述文件module-info.java文件中写如下配置:
模块导出:exports 包a
服务提供:provides 接口a with 类a/类b - 在模块b的描述文件module-info.java中添加如下配置:
声明服务接口:uses 接口a; - 在模块b的类中使用接口a提供的服务
图示:
ServiceLoader
- 在java.util包下 使用需要导包
- public final class ServiceLoader< S > extends Object implements Iterable< S>说明是最终类,且可以使用增强for循环遍历
- 一种加载服务实现的工具
- 获取服务装载程序 =》 通过 ServiceLoader的静态方法load
例子:ServiceLoader.load(服务名.class)参数是服务的class对象
代码:
测试类:package itiheima315; import itiheima3.MyService; import java.util.ServiceLoader; public class Test2 { public static void main(String[] args) { // 加载服务 ServiceLoader<MyService> myService = ServiceLoader.load(MyService.class); // 遍历服务 for(MyService my:myService){ my.service(); } } }
接口:
package itiheima3; public interface MyService { void service(); }
实现类:
package itiheima3.impl; import itiheima3.MyService; public class Czxy implements MyService { @Override public void service() { System.out.println("you are my pretty sunshine"); } } public class itiheima implements MyService { @Override public void service() { System.out.println("唯有不断学习,才能缓解焦虑不安"); } }
module-info.java配置文件:
// 导入接口、实现类 import itiheima3.MyService; import itiheima3.impl.itiheima; import itiheima3.impl.Czxy; // 模块MyOne配置 module MyOne { // 导出包 exports itiheima1; exports itiheima3; // 服务实现:通过provides为服务接口指定实现类 // provides MyService with itiheima; provides MyService with Czxy; }
-
反射机制、class、反射获取构造方法;junit单元测试-测试、使用、@Before和@After;注解-作用分类、常见...
2021-02-11 16:29:36反射-类的加载;反射机制、class、反射获取构造方法;junit单元测试-测试、使用、@Before和@After;注解-作用分类、常见注解、自定义注解、元注解文章目录
1 java的反射
1.1 类的加载
-
程序在运行的时候如果需要使用某个类,该类如果还没有被加载到内存中。
系统会通过加载,链接,初始化 三个步骤来实现对这个类的初始化。 -
加载:
把class文件读入到内存中, 对应的就创建了 Class 的对象。
任何类使用的时候都会创建一个Class对象。 -
链接:
验证:内部结构是否正确,和其他类协调一致。
准备:给类的静态成员分配内存,设置默认值
解析:把类中的二进制数据中的符号编程直接引用 -
初始化:就是我们之前说过的初始化步骤
-
类的初始化时机
创建对象
访问类的静态变量,给静态变量赋值
调用静态方法
初始化某个类的子类
使用java命令运行某个类的时候
使用反射来创建某个类对象
1.2 类加载器
-
把class文件加载到内存中,创建 Class 的对象。
我们不需要关系它到底是如何加载的,了解一下能帮助我们更好的理解程序的运行。 -
组成:
根类加载器Bootstrap ClassLoader
也叫做引导类加载器,主要负责java核心类的加载
比如 System, String 等。 jdk中lib目录下 rt.jar
扩展类加载器Extensions ClassLoader
主要负责扩展类的加载。
jdk中lib目录下 ext 下面
系统类加载器System ClassLoader
主要负责jvm启动的时候加载来自 java命令的class文件。 -
以后只要知道这些东西都是怎么来的。
-
你要明白一点,执行某个代码的时候,这个代码以及在这个代码中用到class 文件都会被加载到内存中。
假如我们现在在内存中站着,面前是一堆字节码文件。如何使用这些字节码文件呢?
字节码文件也是根据类编译得到的二进制文件。 类中有 成员变量,成员方法,构造方法。我们能看懂
也就是说字节码文件中有 成员变量,成员方法,构造方法。但是这些东西在字节码文件中我们看不懂。
反射研究的就是 我们在内存中如何使用字节码文件。
2 反射
2.1 java的反射机制
-
java的反射机制是在运行状态中,对于任意一个对象,都可以获取这个类的多有属性和方法。
对于任意一个对象,都能够调用它的任意一个方法和属性。这种动态获取信息以及动态调用对象
方法的功能称为 java的反射机制。 -
我们想要在内存使用字节码文件,先要获取这个类的字节码文件对象。然后使用
Class类中的方法了。获取字节码文件对象,其实就是获取一个Class类的对象。 -
反射就是通过class 字节码文件对象,使用文件中的成员变量,成员方法,构造方法。
2.2 Class
- Class类的对象表示正在运行的Java应用程序中的类和接口的字节码文件对象
- 字节码文件是有jvm执行的,所以字节码文件对象也是由jvm构建的。
package com.momo.demo; import com.momo.domain.Stu; /* * Stu s = new Stu(); * s.show(); * * 反射的使用方式 * 先要获取class字节码文件对象,其实也就是获取到了Class类的对象 * Class类: * 成员变量:Field * 构造方法:Constructor * 成员方法:Method * 如何获取class对象? 获取方式: * 1, Object类中有Class getClass() 方法 * 2, 数据类型的静态属性 class * 3, Class类的静态方法:static Class forName(String className) * 开发应该用那种? * 自己玩随意用。 * 开发用第三种。 为什么? * 因为第三种要的是一个字符串,不是具体的类名。 * 可以把字符串将来配置到配置文件中。 * */ public class Demo1 { public static void main(String[] args) throws ClassNotFoundException { //方式一 Stu s1 = new Stu(); Class c1 = s1.getClass(); Stu s2 = new Stu(); Class c2 = s2.getClass(); System.out.println(s1==s2);//false System.out.println(c1==c2);//true System.out.println("-------------"); //方式二 Class c3 = Stu.class; /*int.class; String.class;*/ System.out.println(c1==c3); System.out.println("-------------"); //方式三 //包名+类名 Class c4 = Class.forName("com.momo.domain.Stu"); System.out.println(c1==c4); } }
package com.momo.domain; public class Stu { private String name; int age; public String address; public Stu(){} Stu(String name,int age){ this.name = name; this.age = age; } private Stu(String name){ this.name = name; } public Stu(String name,int age,String address){ this.name = name; this.age = age; this.address = address; } public void show(){ System.out.println("public show"); } public void method(String s){ System.out.println("public:"+s); } public String getString(String s,int i){ return s+"---"+i; } private void fun(){ System.out.println("private fun"); } private void fun2(String s){ System.out.println("private fun:"+s); } @Override public String toString() { return "Stu{" + "name='" + name + '\'' + ", age=" + age + ", address='" + address + '\'' + '}'; } }
2.3 使用反射获取构造方法并使用
-
获取构造方法对象的方法
Constructor getConstructor(类<?>… parameterTypes)
获取指定参数的公共的构造方法对象
Constructor<?>[] getConstructors()
获取所有公共构造 方法对象的数组
Constructor getDeclaredConstructor(类<?>… parameterTypes)
可以获取任意一个指定的构造方法,包括私有
Constructor<?>[] getDeclaredConstructors()
获取所有构造 方法对象的数组,包括私有 -
使用构造方法对象创建对象
T newInstance(Object… initargs)
使用此 Constructor对象表示的构造函数,使用指定的初始化参数来创建和初始化构造函数的声明类的新实例。
package com.momo.demo; import com.momo.domain.Stu; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; /* * 反射获取构造方法对象并使用 * */ public class Demo4 { public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { //获取字节码文件对象 Class<?> c = Class.forName("com.momo.domain.Stu"); //getConstructors() 获取所有公共构造方法对象 /* Constructor<?>[] cons = c.getConstructors(); for(Constructor con:cons){ System.out.println(con); } System.out.println("-------------------"); //getDeclaredConstructors() 获取所有构造方法对象 Constructor<?>[] cons2 = c.getDeclaredConstructors(); for(Constructor con:cons2){ System.out.println(con); } System.out.println("---------------------");*/ //获取指定参数的公共的构造方法对象 //getConstructor(...) 参数表示要获取的构造方法对象的参数个数,以及参数类型的class Constructor<?> con = c.getConstructor(); //NoSuchMethodException // Constructor<?> con = c.getConstructor(String.class,int.class); System.out.println(con); Object o = con.newInstance(); /* Stu s = (Stu)o; s.show(); */ System.out.println("-----------------------"); //getDeclaredConstructor(...) 可以获取任意一个指定的构造方法,包括私有 /* Constructor<?> con2 = c.getDeclaredConstructor(); System.out.println(con2); Object o1 = con2.newInstance(); System.out.println(o1);*/ //获取的是私有构造,不能直接使用创建对象。 一会有办法。 Constructor<?> con2 = c.getDeclaredConstructor(String.class); System.out.println(con2); Object o1 = con2.newInstance("aaa"); System.out.println(o1); } }
package com.momo.demo; import com.momo.domain.Stu; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; /* * 反射获取不同的构造方法,创建对象 * */ public class Demo5 { public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { //获取字节码文件对象 Class c = Class.forName("com.momo.domain.Stu"); //public Stu(String name,int age,String address) /* Constructor con = c.getConstructor(String.class, int.class, String.class); Object o = con.newInstance("默默", 18, "长安"); System.out.println(o);*/ System.out.println("---------------"); //private Stu(String name) Constructor con = c.getDeclaredConstructor(String.class); //System.out.println(con); //IllegalAccessException 非法的访问异常 //因为获取的是私有的,编译器会进行语法检查。 //我们可以取消编译期检查 con.setAccessible(true);//true 表示取消检查 Object o = con.newInstance("默默"); System.out.println(o); } }
2.4 使用反射获取成员变量并使用
Field getField(String name)
返回一个 Field对象,它反映此表示的类或接口的指定公共成员字段 类对象。
Field[] getFields()
返回包含一个数组 Field对象反射由此表示的类或接口的所有可访问的公共字段 类对象
Field getDeclaredField(String name)
返回一个 Field对象,它反映此表示的类或接口的指定已声明字段 类对象。
Field[] getDeclaredFields()
返回的数组 Field对象反映此表示的类或接口声明的所有字段 类对象。- 使用的方法
Object get(Object obj)
返回该所表示的字段的值 Field ,指定的对象上。
void set(Object obj, Object value)
将指定对象参数上的此 Field对象表示的字段设置为指定的新值。
package com.momo.demo; import java.lang.reflect.Field; /* * 使用反射获取成员变量对象 * */ public class Demo6 { public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException { Class c = Class.forName("com.momo.domain.Stu"); //Field[] getFields() 获取所有公共的成员变量 Field[] fields = c.getFields(); for(Field f:fields){ System.out.println(f); } System.out.println("------------"); //getDeclaredFields() 获取所有的成员变量,包括私有 Field[] fields1 = c.getDeclaredFields(); for(Field f:fields1){ System.out.println(f); } System.out.println("------------"); //Field getField(String name) 获取指定名字的成员变量对象(公共) //NoSuchFieldException // Field f = c.getField("age"); /* Field f = c.getField("address"); System.out.println(f);*/ //可以获取任意成员变量,包括私有 //Field getDeclaredField(String name) Field f = c.getDeclaredField("name"); System.out.println(f); } }
package com.momo.demo; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; /* * 反射获取成员变量对象 并使用 * Stu s = new Stu(); * s.address = "地址" * */ public class Demo7 { public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { Class c = Class.forName("com.momo.domain.Stu"); //获取公共的address Field f = c.getField("address"); //赋值的时候给指定对象的成员变量赋值,需要一个对象 //获取构造方法对象 Constructor con = c.getConstructor(); Object o = con.newInstance(); System.out.println(o); //赋值 set(Object obj, Object value) //是Field对象的方法,参数一表示对象,参数二表示要赋的值 //给那个对象的这个成员变量赋值 f.set(o,"北京");//表示要给o对对象的f成员变量赋值 北京 System.out.println(o); System.out.println("-----------------"); //获取name并赋值 //IllegalAccessException Field fname = c.getDeclaredField("name"); //取消访问检查 fname.setAccessible(true); fname.set(o,"默默"); System.out.println(o); System.out.println("-----------"); Field fage = c.getDeclaredField("age"); //取消访问检查 fage.setAccessible(true); fage.set(o,18); System.out.println(o); System.out.println("-----------------"); //Object get(Object obj) System.out.println(f.get(o)); System.out.println(fname.get(o)); System.out.println(fage.get(o)); } }
2.5 使用反射获取成员方法并使用
- Method getMethod(String name, 类<?>… parameterTypes)获取指定名称的公有的方法,第二个参数表示要获取的方法的参数的个数,以及参数类型的class
- Method[] getMethods()获取所有公共的方法,包括父类的
- Method getDeclaredMethod(String name, 类<?>… parameterTypes)获取指定名称的方法对象,第二个参数表示要获取的方法的参数的个数,以及参数类型的class
- Method[] getDeclaredMethods()获取所有的成员方法,不包括父类
- 使用成员方法对象的方法
Object invoke(Object obj, Object… args)
第一个参数表示要使用那个对象调用当前这个方法,第二个参数表示,调用这个方法的时候需要传递的实际参数
package com.momo.demo; import java.lang.reflect.Method; /* * 获取成员方法对象 * */ public class Demo8 { public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException { Class c = Class.forName("com.momo.domain.Stu"); //Method[] getMethods() 获取所有公共的方法,包括父类的 Method[] methods = c.getMethods(); for(Method m:methods){ System.out.println(m); } System.out.println("----------------"); //Method[] getDeclaredMethods() 获取所有的成员方法,不包括父类 Method[] methods = c.getDeclaredMethods(); for(Method m:methods){ System.out.println(m); } System.out.println("----------------"); //方法 getMethod(String name, 类<?>... parameterTypes) //获取指定名称的公有的方法,第二个参数表示要获取的方法的参数的个数,以及参数类型的class /* Method mshow = c.getMethod("show"); System.out.println(mshow);*/ Method method = c.getMethod("method", String.class); System.out.println(method); System.out.println("----------------"); //方法 getDeclaredMethod(String name, 类<?>... parameterTypes) //获取指定名称的方法对象,第二个参数表示要获取的方法的参数的个数,以及参数类型的class Method mfun = c.getDeclaredMethod("fun"); System.out.println(mfun); } }
package com.momo.demo; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; /* * 反射获取成员方法对象 并使用 * */ public class Demo9 { public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { Class c = Class.forName("com.momo.domain.Stu"); Constructor con = c.getConstructor(); Object o = con.newInstance(); //public void show(); Method mshow = c.getMethod("show"); //Object invoke(Object obj, Object... args) //第一个参数表示要使用那个对象调用当前这个方法 //第二个参数表示,调用这个方法的时候需要传递的实际参数 //Object 表示方法的返回值 Object invoke = mshow.invoke(o); System.out.println(invoke); System.out.println("---------------"); //public void method(String s) Method method = c.getMethod("method", String.class); Object abad = method.invoke(o, "abad"); System.out.println(abad); System.out.println("---------------"); // public String getString(String s,int i) Method getmethod = c.getMethod("getString", String.class, int.class); Object asdf = getmethod.invoke(o, "asdf", 111); System.out.println(asdf); System.out.println("---------------"); //private void fun() Method fun = c.getDeclaredMethod("fun"); fun.setAccessible(true); Object invoke1 = fun.invoke(o); System.out.println(invoke1); } }
3 junit单元测试
3.1 测试
- 分类:
黑盒测试: 不需要写代码,给定输入值,看程序是否输出了期望值。
白盒测试: 需要写代码,需要关注代码的执行流程。
3.2 使用
- 有一个类需要测试 MyMath — sum(int a,int b)
- 步骤:
创建一个测试包,定义一个测试类 MyMathTest
定义测试方法 sumTest() 返回值 void 空参
需要让这个方法可以独立运行 需要给方法上加上 @Test
如果报错了, 需要导入juint 的 jar包
执行方法,对结果进行判断 。 红色表示失败。 绿色表示成功
一般我们会使用 断言
package com.momo.test; import com.momo.domain.MyMath; import org.junit.Assert; import org.junit.Test; public class MyMathTest { @Test public void sumTest(){ System.out.println("我执行了。。。。"); //创建对象 MyMath mm = new MyMath(); //调用方法 int result = mm.sum(2, 3); //System.out.println(result); //断言 我认为结果应该是什么 Assert.assertEquals(5,result); } }
3.3 @Before 和 @After
package com.momo.test; import com.momo.domain.MyMath; import org.junit.*; public class MyMathTest { @BeforeClass public static void aaa(){ System.out.println("aaa"); } @AfterClass public static void bbb(){ System.out.println("bbb"); } /* * 初始化方法: 申请一些资源。。。。 * 希望这个方法每次都可以在测试方法执行之前自动执行 * */ @Before public void init(){ System.out.println("进行了一些初始化。。。。"); } /* * 释放资源的方法: * 希望这个方法每次都可以在测试方法执行完成之后自动执行 * */ @After public void close(){ System.out.println("释放了一些资源。。。。"); } @Test public void divTest(){ //创建对象 MyMath mm = new MyMath(); int result = mm.div(10,2); Assert.assertEquals(5,result); } @Test public void sumTest(){ System.out.println("我执行了。。。。"); //创建对象 MyMath mm = new MyMath(); //调用方法 int result = mm.sum(2, 3); //System.out.println(result); //断言 我认为结果应该是什么 Assert.assertEquals(5,result); } }
4 注解
4.1 说明程序的,给计算机看的。
- 注解 Annotation, 也叫做元数据。一种代码级别的说明。
是jdk5之后出现的。 和 类,接口 是同一个级别的。 - 它可以声明在 包 类 方法 字段 等前面。用来对这些东西进行说明。
- 注解的使用 @注解名称
4.2 作用分类:
- 代码分析:用到反射
- 编译检查:@override
- 编写文档:@return @param
- 替代配置文件: xml 框架部分
- 定义注解:元注解,注解上的注解
4.3 java中的几个注解
- @Override:表示方法是从类类继承过来的。执行编译检查。
- @SuppressWarnings(): 抑制警告 值比较多 all
- @Deprecated:表示方法不建议使用。
package com.momo.domain; import java.util.List; @SuppressWarnings("all") public class Student { private String name; private int age; @SuppressWarnings("all") private int tele; /*@SuppressWarnings("all")*/ private List list; public Student() { } public Student(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String toString() { return "Student{" + "name='" + name + '\'' + ", age=" + age + '}'; } @Deprecated public void show(){ System.out.println("show"); } public void show(String s){ System.out.println("show"+s); } }
package com.momo.demo; import com.momo.domain.Student; public class Demo11 { public static void main(String[] args) { Student s = new Student(); s.show(); s.show("aaa"); } }
4.4 自定义注解
-
格式:
public @interface MyAnnotation {} -
注解属性
注解本身相当于就是一个接口,接口中有常量和抽象方法。
抽象方法在注解中就叫做 注解属性 -
注解属性的类型:
基本类型
String
Class
Annotation
Enum 枚举
这几个类型的数组 -
注意事项:
注解中一旦有了注解属性,使用注解的时候必须赋值。除非这个属性有默认值。
package com.momo.demo; import com.momo.domain.Stu; import java.util.Date; public @interface MyAnnotation { //注解属性 int i(); short s(); boolean boo(); char c(); // Stu stru(); //Date date(); String str(); Class cc(); aaaa abc(); String[] strs(); }
package com.momo.domain; public @interface MyAnntation2 { String s(); int i(); }
package com.momo.demo; import com.momo.domain.MyAnntation2; @MyAnntation2(s="aaa",i=12) public class Demo12 { public static void main(String[] args) { /* @MyAnntation2(s="aaa")*/ int i = 5; } /* @MyAnntation2(s="aaa")*/ public void show(){ } }
- 给注解属性赋值的格式:
@注解名(属性名=属性值)
如果注解属性的类型是数组,如何赋值
属性名={属性值}
如果只有一个值
属性名=属性值
package com.momo.domain; public @interface MyAnntation2 { /* String s(); int i();*/ int[] arr(); }
package com.momo.demo; import com.momo.domain.MyAnntation2; /*@MyAnntation2(s="aaa",i=12)*/ //@MyAnntation2(arr={12,34,56,555}) @MyAnntation2(arr=12) public class Demo12 { public static void main(String[] args) { /* @MyAnntation2(s="aaa")*/ int i = 5; } /* @MyAnntation2(s="aaa")*/ public void show(){ } }
- 特殊情况
如果属性名是value,并且只需要给value赋值的时候。
这个时候value 这个属性名可以省略不写。
package com.momo.demo; import com.momo.domain.MyAnnotation3; /*@MyAnnotation3(i=1)*/ /*@MyAnnotation3(1)*/ @MyAnnotation3(value=12) public class Demo13 { } package com.momo.domain; public @interface MyAnnotation3 { /* int i();*/ int value(); }
4.5 元注解
- 定义在注解上的注解
@Retention 作用是规定注解保留到什么阶段
值是 RetentionPolicy中的
CLASS 在代码中的字节码文件中保留
RUNTIME 所有阶段都保留
SOURCE 只在源码中保留
@Target 规定注解可以用在什么上面
值是 ElementType 中的
TYPE: 用在类,接口等
METHOD:用在方法
FIELD:用在字段
…package com.momo.demo; import com.momo.domain.MyAnnotation3; /*@MyAnnotation3(i=1)*/ @MyAnnotation3(1) /*@MyAnnotation3(value=12)*/ public class Demo13 { /* @MyAnnotation3(value=12)*/ public static void main(String[] args) { /* @MyAnnotation3(value=12)*/ int i = 5; System.out.println(i); } }
package com.momo.domain; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(ElementType.TYPE) /*@Target(ElementType.LOCAL_VARIABLE)*/ /*@Retention(RetentionPolicy.SOURCE)*/ @Retention(RetentionPolicy.RUNTIME) public @interface MyAnnotation3 { /* int i();*/ int value(); }
-
练习:模拟junit测试 @Test功能
自定义一个注解@MyAnnotation 在另一个类中可以执行执行带有@MyAnnotation
注解的方法分析:
定义一个注解
在注解上添加对应的元注解
在另一个类中给方法上添加注解
当执行这个类main的时候 可以自动执行带有@MyAnnotation的方法提示要使用反射
package com.momo.demo; import java.lang.reflect.Method; public class Demo14 { public static void main(String[] args) throws ClassNotFoundException { Class<?> c = Class.forName("com.momo.domain.MyMath"); //获取所有方法 Method[] methods = c.getMethods(); //遍历 for(Method m:methods){ //判断方法是否带有指定的注解 boolean boo = m.isAnnotationPresent(MyAnnotation.class); if(boo){ m.invoke(对象,参数); } } } }
-
-
java中的反射机制,以及如何通过反射获取一个类的构造方法 ,成员变量,方法,详细。。
2021-02-28 19:20:44首先先说一下类的加载,流程。只有明确了类这个对象的存在才可以更好的理解反射的原因,以及反射的机制。... 而这个对象就是我们反射中将要使用的对象。任何类被使用时系统都会建立一个Class对象。连接验证...首先先说一下类的加载,流程。只有明确了类这个对象的存在才可以更好的理解反射的原因,以及反射的机制。
一、 类的加载
当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载,连接,初始化三步来实现对这个类进行初始化。
加载
就是指将class文件读入内存,并为之创建一个Class对象---> 而这个对象就是我们反射中将要使用的对象。
任何类被使用时系统都会建立一个Class对象。
连接
验证 是否有正确的内部结构,并和其他类协调一致;
准备 负责为类的静态成员分配内存,并设置默认初始化值;
解析 将类的二进制数据中的符号引用替换为直接引用。
初始化 就是常规的一个类的的初始化步骤,有静态先静态。。。。忘记的自己回去看~
二、类初始化时机
·创建类的实例
·访问类的静态变量,或者为静态变量赋值
·调用类的静态方法
·使用反射方式来强制创建某个类或接口对应的java.lang.Class对象
·初始化某个类的子类
·直接使用java.exe命令来运行某个主类
三.类加载器
l 类加载器
负责将.class文件加载到内在中,并为之生成对应的Class对象。
虽然我们不需要关心类加载机制,但是了解这个机制我们就能更好的理解程序的运行。
l 类加载器的组成
Bootstrap ClassLoader 根类加载器
Extension ClassLoader 扩展类加载器
Sysetm ClassLoader 系统类加载器
四、类加载器的作用
1. Bootstrap ClassLoader 根类加载器
也被称为引导类加载器,负责Java核心类的加载
比如System,String等。在JDK中JRE的lib目录下rt.jar文件中
2.Extension ClassLoader 扩展类加载器
负责JRE的扩展目录中jar包的加载。
在JDK中JRE的lib目录下ext目录
3. Sysetm ClassLoader 系统类加载器 (我们自己定义的类一般在这里)
负责在JVM启动时加载来自java命令的class文件,以及classpath环境变量所指定的jar包和类路径
//------------------------------------------------代码的分割线
//下面首先介绍的是第一步如何通过Class的方法来获取指定类的class对象,没有该对象就无从反射谈起
1 /*
2 * 反射:就是通过class文件对象,去使用该文件的成员变量,构造方法,成员方法。
3 * 在以前的时候一般都是通过创建对象然后通过对象取调用该类的方法4 *5 * Class类:通过Class来得到被加载类的三个属性同时每一个属性又通过对应的类进行创建对象来调用方法来使用被加载类的方法6 * 成员变量: Field7 * 构造方法: Constructor8 * 成员方法: Method9 *10 * 获取class文件对象的方式:11 * A:object类的getClass()方法12 * B:数据类的静态属性class(只要是数据类型都有这个方法)13 * C:class类中的静态方法14 * public static Class> forName(String className)15 * String className:此处需要的是类的带包名的全路径否则报错,java.lang.ClassNotFoundException: Person16 *17 *18 * 实际开发的时候一般使用的是第三种。因为该方法使用的是配置文件,可以在配置文件中直接进行配置,可以让该字符串变化。19 */
20 public classReflectDemo {21 public static void main(String[] args) throwsClassNotFoundException {22 //方式1
23 Person p = newPerson();24 Class c =p.getClass();25
26 Person p2 = newPerson();27 Class c2 =p.getClass();28
29 System.out.println(p ==p2);30 System.out.println(c ==c2);31
32 //方式2
33 Class c3 = Person.class;34
35 //方式336 //反射机制.Person 这里很容易出错,我自己写的时候就经常不小心写错。写错了还不好检查。
37 /*
38 * 由于写一个类的全路径经常会出错,所以尽量不自己手写,39 * 1.点开那个类里面有一个copy qualifield Name的选项,40 * 2.直接在外面写通过辅助自动补齐手段进行检测,在需要的那个类里面找到package。41 */
42 Class c4 = Class.forName("反射机制.Person");43 System.out.println(c ==c4);44
45 }46 }
//---------------------------------------------------------
//下面给出上面再main方法中使用的person类,相当简单的一个类
//在后面会陆续使用该类,进行构造方法,字段,以及成员方法的调用,后面就不再给出该类,该类一直代表person
1 public classPerson {2 privateString name;3 intage;4 publicString address;5
6 publicPerson() {7 }8
9 privatePerson(String name) {10 this.name =name;11 }12
13 Person(String name, intage) {14 this.name =name;15 this.age =age;16 }17
18 public Person(String name, intage, String address) {19 this.name =name;20 this.age =age;21 this.address =address;22 }23
24 public voidshow() {25 System.out.println("show");26 }27
28 public voidmethod(String s) {29 System.out.println("method " +s);30 }31
32 public String getString(String s, inti) {33 return s + "---" +i;34 }35
36 private voidfunction() {37 System.out.println("function");38 }39
40 @Override41 publicString toString() {42 return "Person [name=" + name + ", age=" + age + ", address=" +address43 + "]";44 }45 }
//---------------------------------------------
//通过反射获取一个类的构造方法并进行使用。包括如何访问Person类的公共无参构造方法,公共有参构造方法,以及私有带参构造
1 importjava.lang.reflect.Constructor;2 importjava.lang.reflect.InvocationTargetException;3
4 /*
5 * 通过反射机制获取构造方法6 */
7 public classReflectDemo {8 public static void main(String[] args) throwsException {9 //获取字节码文件对象,此处仍然是上面的person类.
10 Class c = Class.forName("反射机制.Person");11
12 //获取构造方法13 //public Constructor [] getConstructors() => 公共构造方法所有的14 //public Constructor [] getDeclaredConstructors() => 所有的构造方法 ,一旦添加Declared单词修饰获取一般是全部的声明包括自有private,protected。15
16 //Constructor[] cons = c.getConstructors();
17 Constructor[] cons = c.getDeclaredConstructors(); //这种方法虽然获取到了所有的构造方法,但是没什么作用,因此需要使用下面的方法获取单个的构造方法
18 for(Constructor con : cons) {19 System.out.println(con);20 }21
22 //获取单个构造方法,23 //public Constructor getConstructor(Class>... parameterTypes)24 //该方法的参数表示的是:你要获取的构造方法的构造参数的个数以及class字节码文件对象
25
26 Constructor con =c.getConstructor();27
28 //使用此 Constructor 对象表示的构造方法来创建该构造方法的声明类的新实例,并用指定的初始化参数初始化该实例。29 //public T newInstance(Object... initargs)
30
31 Object obj = con.newInstance(); //此处创建的是Person类的对象实例,//此处创建的是Person类的对象实例,由于是无参构造因此不需要参数。
32 System.out.println(obj); //Person [name=null, age=0, address=null]
33 /*
34 * 下面的代码是:通过反射区获取带参数的构造方法35 */
36 //获取指定参数的构造函数37 //public Constructor getConstructor(Class>... parameterTypes)
38 /*
39 * 返回一个 Constructor 对象,它反映此 Class 对象所表示的类的指定公共构造方法。 parameterTypes 参数是 Class 对象的一个数组,40 */
41 Constructor con2 =c.getConstructor(String.class, int.class,42 String.class);43
44 //通过构造方法创建对象45 //ublic T newInstance(Object... initargs)
46 Object obj2 = con.newInstance("java", 22, "武汉");47 System.out.println(obj2);48 //----------------------------------49 //通过反射获取私有构造方法并使用。50 //获取私有构造器 ---> getDeclaredConstructor51 //如果直接使用getConstructor则会导致没有该方法的异常NoSuchMethodException
52 Constructor con3 = c.getDeclaredConstructor(String.class);53
54 //私有方法直接访问:--->报错IllegalAccessException非法访问异常
55 /*
56 * 暴力访问 public void setAccessible(boolean flag) 将此对象的 accessible57 * 标志设置为指示的布尔值。 值为 true 则指示反射的对象在使用时应该取消 Java 语言访问检查。 值为 false58 * 则指示反射的对象应该实施 Java 语言访问检查。59 */
60
61 con3.setAccessible(true); //值为 true 则指示反射的对象在使用时应该取消 Java 语言访问检查
62 Object obj3 = con.newInstance("android");63 System.out.println(obj3);64
65 }66 }
//-------------------------------------------------
//下面的代码是如何通过反射获取一个类的公有成员变量私有成员变量,并进行赋值显示
1 importjava.lang.reflect.Constructor;2 importjava.lang.reflect.Field;3
4 /*
5 * 通过反射获取成员变量并使用6 */
7 public classReflectDemo {8 public static void main(String[] args) throwsException {9 //获取字节码文件class对象
10 Class c = Class.forName("反射机制.Person");11
12 //获取所有的成员变量,13 //Field[] fields = c.getFields(); 获取所有的公共成员变量,放在数组中不易操作。14 //Field[] fields = c.getDeclaredFields(); 获取所有成员变量,放在数组中不易操作。15
16 //通过无参构造方法获取对象,有了对象才好对成员变量进行赋值,即使在反射中成员变量仍然是属于对象的。
17 Constructor con =c.getConstructor();18 Object obj =con.newInstance();19 //System.out.println(obj);20
21 //获取单个的成员变量,指定成员变量的名字---address
22 Field addressField = c.getField("address");23
24 //对于成员变量,一般是通过对象 : obj.address = "777"进行赋值,25 //public void set(Object obj, Object value)26 //上面方法的意思是将指定对象变量obj上此 Field 对象addressField表示的字段设置为指定的新值value 。
27
28 addressField.set(obj, "java"); //给obj对象的addressField字段设置值为value:java,这个地方很拗口,与java基本思路背道而驰。
29 System.out.println(obj);30
31 //获取name并对其赋值,name是私有成员变量。
32 Field nameField = c.getDeclaredField("name");33 //IllegalAccessException,暴力访问
34 nameField.setAccessible(true);35 nameField.set(obj, "android");36 System.out.println(obj);37
38 //获取age并对其赋值,age是私有成员变量。
39 Field ageField = c.getDeclaredField("age");40 ageField.setAccessible(true);41 ageField.set(obj, 25);42 System.out.println(obj);43
44 }45 }
//-------------------------------------------
// 获取类中的所有方法,公共方法,私有方法。并进行使用。
1 importjava.lang.reflect.Constructor;2 importjava.lang.reflect.Method;3
4 /*
5 * 获取类中的所有方法6 */
7 public classReflectDemo {8 public static void main(String[] args) throwsException {9
10 //获取字节码class对象
11 Class c = Class.forName("反射机制.Person");12
13 //获取所有方法14 //Method[] methods = c.getMethods();//该方法获取获取本身的以及父亲的所有公共方法,此处无意义。
15 Method[] methods5 = c.getDeclaredMethods(); //获取自己的所有方法,加入了Declared,在实际使用中可以不管你需要的东西的公共还是私有直接使用Declared修饰的方法16 //for (Method method : methods) {17 //System.out.println(method);18 //}19
20 //获取对象,由于其它构造麻烦一些,在获取对象时一律使用无参构造。
21 Constructor con =c.getConstructor();22 Object obj =con.newInstance();23
24 //获取单个的指定方法25 //public Method getMethod(String name, Class>... parameterTypes)26 //第一个参数表示的是方法名,第二个参数表示的方法的参数class类型。
27 // show方法是公共,无形参的方法public void show() { }
28 Method method1 = c.getDeclaredMethod("show");29 //public Object invoke(Object obj, Object... args)30 // 返回值是object接受,第一个参数表示对象是谁,第二个参数表示调用该方法的实际参数(形参)。
31 // 没有返回值就不写,
32 method1.invoke(obj); //调用obj对象的mehod1方法,33
34 //带一个参数方法无返回值的方法调用, public void method(String s){}
35 Method method2 = c.getMethod("method", String.class);36 method2.invoke(obj, "java");37
38 //带两个形参,有返回值的方法的调用public void method(String s),
39 Method method3 = c.getMethod("getString", String.class,40 int.class);41 Object objStr = method3.invoke(obj, "java", 50);42 System.out.println("Method3" +objStr);43
44 //私有方法
45 Method method4 = c.getDeclaredMethod("function");46 method4.setAccessible(true); //暴力访问47 method4.invoke(obj);48
49 }50 }
-
day15(完结)【反射(类加载器、反射)、反射获取构造方法-成员变量-成员方法、模块化(概述、模块基本使用、...
2020-08-28 17:14:152.3、反射获取构造方法并使用 2.4、反射获取构造方法并使用练习 2.5、反射获取成员变量并使用 2.6、反射获取成员变量并使用练习 2.7、反射获取成员方法并使用 2.8、反射获取成员方法并使用练习 2.9、反射练习 1.1、... -
细说反射,Java 和 Android 开发者必须跨越的坎
2017-07-06 23:36:13但是,我已经写了注解和动态代理这两个知识点的博客,阅读量还可以,这两个知识点是属于反射机制中的,现在对于注解和动态代理息息相关的反射知识基础我倒是退缩了,所以说看起来很普通的东西,其实真的要一五一十地... -
【Java基础】反射;Java类加载以及反射
2021-06-07 22:05:33十、反射 类加载 当程序要使用某个类时,如果该类还没有被加载到内存中时,该系统会通过类的加载、类的连接、类的初始化三个步骤来对类进行初始化。如果没有出现意外,JVM会连续完成这三个步骤,所以有时也将这三个... -
反射问题研究02
2021-05-19 18:00:56反射创建对象的四种方式反射机制反射可以提供的功能反射的优缺点反射主要的APIClass类和Class对象Class类Class对象获取Class对象的五种方式Class对象的细节补充Class类的常用方法都有哪些类可以有Class对象反射获取... -
java 类,基本类型,反射,内省
2021-02-27 13:17:21基本数据类型的数组必须维数和类型相同得到的字节码类型才相同: 2、java反射,首先反射能够通过类名获取类的字节码,然后获取各个属性,方法,构造方法等类的一切。如下方法:得到了类对象,方法和属性当然可以对... -
day16_反射&Lambda
2022-04-11 15:42:45} } 3.3.4 反射获取构造方法并使用案例 案例需求 通过反射获取私有构造方法并创建对象 代码实现 学生类 public class Student { //成员变量:一个私有,一个默认,一个公共 private String name; int age; public ... -
类加载、反射以及jdk9的模块化服务
2019-12-22 22:26:45当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过类的加载,类的连接,类的初始化这三个步骤来对类进行初始化。如果不出现意外情况,JVM将会连续完成这三个步骤,所以有时也把这三个步骤统称为类... -
Java类加载器与反射机制
2021-03-20 20:39:13反射2.1反射的概述2.2获取Class类对象的三种方式2.3反射获取构造方法并使用2.4反射获取构造方法2.5反射获取构造方法2.6反射获取成员变量并使用2.7反射获取成员变量并使用练习2.8反射获取成员方法并使用【应用】2.9... -
Java核心(三)反射
2017-05-14 01:35:26Java反射给我们提供了在运行时检查甚至修改应用行为的机制。 反射是java高级的核心技术,所有有经验的程序员都应该理解...Java中的反射Java的反射是一种很强大的机制,在正常的编程中使用并不多,但它是java的主干,很 -
Java进阶——详解反射机制及应用中的泛型类型的获取和曾经踩过的一个坑
2017-05-13 12:23:20可以获知某个已知类或已知对象的的相关信息(包括类的方法、属性、父类等信息,还包含创建实例、判断实例类型等操作)这种动态获取信息以及动态调用对象方法的功能就是Java语言的反射机制 -
测试开发之Python核心笔记(19): 深入理解类的属性
2020-08-13 19:16:25属性作为类的重要组成部分,除了平时常用的读取和设置操作之外,还有很多隐藏的、高级的操作。比如属性的查找顺序、属性的类型检查、限制属性的动态...但是要注意属性和方法不能是私有的,如果是以“_”开头的属性和 -
Stream流、反射、模块化
2022-02-20 14:27:50boolean startsWith(String prefix) 测试此字符串是否以指定的前缀开头。...Stream流的使用 生成流 通过数据源(集合,数组等)list.stream 中间操作 一个流后面可以跟随零个或多个中间操作,其目的主要是打 -
Java知识点汇总--类加载器、反射、 模块化
2021-02-15 14:40:29反射2.1 反射的概述2.2 获取Class类对象的三种方式2.3 反射获取构造方法并使用2.3.1 Class类获取构造方法对象的方法2.3.2 Constructor类用于创建对象的方法2.4 反射获取构造方法并使用练习12.5 反射获取构造方法并... -
Java类加载器和反射机制
2021-10-14 15:24:421.类加载器 1.1类加载【理解】 类加载的描述 当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过类的加载,类的连接,类的初始化这三个步骤来对类...验证阶段:用于检验被加载的类是否有正确的内部结 -
反射、注解、xml、DTD、Schema技术
2020-06-23 22:19:48获取属性和方法1)获取属性2)获取方法二、注解1.注解的介绍2.注解的分类3.内置注解1)@Override2)@Deprecated3)@SuppressWarnings4.自定义注解1)简单入门2)元注解1)@Target2)@Retention(了解)3.@Documented... -
JAVA之类加载器&反射&模块化
2021-09-16 15:24:17反射2.1概述2.2获取对象的三种方式2.2获取构造方法2.3获取成员变量2.4获取成员方法2.5案例3.模块化3.1概述3.2模块服务的基本使用4.反射常见面试题 1.类加载器 1.1类加载 1.类加载的描述 当程序要使用某个类时,如果... -
Java反射
2022-05-24 14:52:19一:类加载器 1.1 类加载 当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过类的加载、类的连接、类的初始化这三个步骤来...验证阶段:用于检验被加载的类是否有正确的内部结构,并和其他类协调一致 -
类加载器和反射
2021-12-30 15:13:422.3反射获取构造方法并使用【应用】 2.4反射获取构造方法并使用练习1【应用】 2.5反射获取构造方法并使用练习2【应用】 2.6反射获取成员变量并使用【应用】 2.7反射获取成员变量并使用练习【应用】 2.8反射获取... -
Java(集合、泛型、反射、注解)
2022-01-08 15:39:18Java(集合、泛型、反射、注解) 1.集合,数据的存储 2.泛型,参数化类型 3.反射,反向追溯 4.注解,框架中简化操作 一.数据结构 1.数组 Char[] cs = new Char[]{'G','U','P','A','O'}; 赋值,相当于初始化了大小。 ... -
java笔记(第七部分类加载与反射)
2021-04-21 08:27:57目录一、类加载器1.1 类加载概述1.2 类加载器1.2.1 作用1.2.2 java内置的类加载器二、反射2.1 反射概述2.2 获取Class类对象的三种方式2.3反射获取类的各个部分2.3.1 反射获取类的构造方法2.3.2 反射创建对象的方法... -
Java反射之Constructor、Method、Field使用及说明
2019-01-16 16:18:36在编程生活中,一般处理业务时不需要使用反射的内容,因为反射属于Java编程中的高级知识。但当我们需要迈向更高级的Java编程时,运行时类型信息是必须深刻理解的,在Spring MVC中,各种注解底层实现的方式就是使用了... -
【JavaSE】类加载器,反射,模块化
2021-01-17 20:24:191.类加载器 1.1类加载【理解】 类加载的描述 当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过类的加载,类的连接,类的初始化这三个步骤来对类...验证阶段:用于检验被加载的类是否有正确的内部结 -
反射机制
2020-12-22 18:54:55* 1、反射机制有什么用? * 通过java语言中的反射机制可以操作字节码文件 * 优点类似于黑客(可以读和修改字节码文件) * 2、反射机制相关类:java.lang.reflect.*; * 3、相关类:(较重要) * java.lang.... -
java中的反射机制,以及如何通过反射获取一个类的构造方法 ,成员变量,方法,详细。。...
2016-04-26 20:26:00首先先说一下类的加载,流程。只有明确了类这个对象的存在才可以更好的理解反射的原因,以及反射的机制。 一、 类的加载 ... 而这个对象就是我们反射中将要使用的对象。 任何类被使用时系统都会建立... -
学习笔记--类加载和反射基础知识
2020-06-21 21:59:341.类加载器 1.1类加载 • 类加载的描述 o 当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过类的加载,类的连接,类的初始化这三个步骤来对类进行初始...o 验证阶段:用于检验被加载的类是否有正确的内 -
通向Golang的捷径【11. 接口和反射】
2019-12-30 17:36:3011.7 示例:Sorter 接口的排序 以下将给出 sort 包的一个应用示例, 为了实现一个数值和字符串集合的排序, 只需获取集合元素的个数, 并使用 Less(i, j) 进行元素 i 和 j 的比较, 以及使用 Swap(i, j), 进行索引 i 和 ...