反射
-
反射机制有什么用?
通过java语言中的反射机制可以操作字节码文件。有点类似于黑客(可以读和修改字节码文件)
通过反射机制可以操作代码片段。(class文件)
-
反射机制的相关类在哪个报下?
java.lang.reflect.*;
-
反射机制相关的重要的类有哪些?
//java.lang.Class :代表整个字节码,代表一个类型,代表整个类 //java.lang.reflect.Method:代表字节码中的方法字节码,代表类中的方法 //java.lang.reflect.Constructor :代表字节码中的构造方法字节码,代表类中的构造方法 //java.lang.reflect.Field :代表字节码中的属性字节码,代表类中的成员变量(静态变量+实例变量) //class public class User{ //Field int no; //Constructor public User(){ } //Method public int getNo(){ return no; } public void setNo(int no){ this.no = no; } }
-
获取类的字节码的三种方式
字节码内存图
package reflect; import java.util.Date; /* 要操作一个类的字节码,需要首先获取到这个类的字节码,怎么获取java.lang.Class实例? 三种方式 第一种:Class c = Class.forName("带有包名的完整类名"); 第二种:class c = 对象.getClass(); 第三种: Class c = 任何类型.class; */ public class ReflectText01 { public static void main(String[] args) { /* Class.forName() 1、静态方法 2、方法的参数是一个字符串 3、字符串需要的是一个完整类名 4、完整类名必须带有包名、java.lang包也不能省略 */ Class c1 = null; Class c2 = null; try { c1 =Class.forName("java.lang.String"); //c1代表String.class 文件,或者说c1代表String类型 c2 =Class.forName("java.util.Date"); //c2代表Data类型 Class c3 =Class.forName("java.lang.Integer"); //c3代表Integer类型 Class c4 =Class.forName("java.lang.System"); //c4代表System类型 } catch (ClassNotFoundException e) { e.printStackTrace(); } //java中任何一个对象都有一个方法:getClass() String s = "abc"; Class x = s.getClass(); //x代表String.class字节码文件,x代表String类型 System.out.println(c1 == x); //true(==判断的是对象的内存地址) Date time = new Date(); Class y = time.getClass(); System.out.println(c2 == y); //true(c2和y两个变量中保存的内存地址都是一样的,都指向方法区中的字节码文件) //第三种方式,java语言中任何一种类型,包括基本数据类型,都有.class类型 Class z = String.class; //z代表String类型 Class a = Date.class; //a代表Date类型 Class b = int.class; //b代表int类型 Class c = double.class; //c代表double类型 System.out.println(x == z); //true System.out.println(y == a); //true } }
-
获取到Class,用newInstance()方法实例化对象
User类
package bean; public class User { public User(){ System.out.println("无参数构造方法执行了"); } }
测试类
package reflect; import bean.User; /* 获取到Class,可以干什么? 通过Class的newInstance()方法来实例化对象 注意:newInstance()方法的内部实际上调用了无参数构造方法,必须保证无参数构造方法存在才行 */ public class ReflectText02 { public static void main(String[] args) { //这是不使用反射机制,创建对象 User user =new User(); System.out.println(user); //使用反射机制的方式创建对象 try { //通过反射机制,获取Class,通过Class来实例化对象 Class c =Class.forName("bean.User");//c代表User类型 //newInstance() 这个方法会调用User这个类的无参构造方法,完成对象的创建 //重点是:newInstance()这个方法调用的是无参构造,必须保证无参构造方法是存在的 Object obj =c.newInstance(); System.out.println(obj); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } } }
-
验证反射机制的灵活性
package reflect; import bean.User; import java.io.FileReader; import java.util.Properties; /* 验证反射机制的灵活性 java代码写一遍,在不改变java源代码的基础上,可以做到不同对象的实例化。非常灵活 (符合OCP开闭原则:对扩展开放,对修改关闭。) 后期要学习的是高级框架,而工作过程中,也都是使用高级框架,包括:ssh ssm Spring SpringMVC MyBatis Spring Struts Hibernate ··· 这些高级框架底层实现原理:都采用了反射机制。所以反射机制还是重要的 学了反射机制有利于理解剖析框架底层的源代码 */ public class ReflectText03 { public static void main(String[] args) throws Exception { //这种方式代码就写死了 User user = new User(); //以下代码是灵活的,代码不需要改动,可以修改配置文件,配置文件修改之后,可以创建出不同的实例对象 //通过IO流读取classinfo.properties文件 FileReader reader = new FileReader("REFLECT\\classinfo.properties"); //创建属性类对象 Properties pro = new Properties(); //key 和 value 都是String类型 //加载 pro.load(reader); //关闭流 reader.close(); //通过key获取value String className = pro.getProperty("className"); System.out.println(className); //通过反射机制实例化对象 Class c =Class.forName(className); Object obj =c.newInstance(); System.out.println(obj); } }
classinfo配置文件
className =java.util.Date #bean.User
-
Class.forName()方法的执行会导致类加载
package reflect; /* 研究一下:Class.forName()发生了什么 重点: 如果你只是希望一个类的静态代码块执行,其他代码一律不执行。 可以使用:Class.forName("带包名的完整类名") 这个方法的执行会导致类加载,类加载时:静态代码块执行. 提示: 后面JDBC的时候,需要使用。 */ public class ReflectText04 { public static void main(String[] args) { try { //Class.forName()这个方法的执行会导致:类加载。 Class.forName("reflect.MyClass"); } catch (ClassNotFoundException e) { e.printStackTrace(); } } } class MyClass{ //静态代码块在类加载的时候执行,并且只执行一次。 static { System.out.println("MyClass类的静态代码块执行了"); } }
-
获取一个文件的绝对路径
package reflect; import java.io.FileReader; /* 研究一下文件路径的问题 怎么获取一个文件的绝对路径,以下讲解的这种方式是通用的。但前提是:文件需要在类路径下,才可以使用。 */ public class AboutPath { public static void main(String[] args) { //这种方式的路径缺点:移植性差,在IDEA默认的当前路径是project的根 //这个代码假设离开了IDEA,到了其他位置,可能当前路径就不是project的根了,这个时候路径就无效了 //FileReader reader = new FileReader("REFLECT\\classinfo.properties"); //接下来说一种比较常用的方法,即使代码换位置了,这样编写依然是通用的 //注意:使用以下通用方式的前提是:这个文件必须在类路径下 //类路径:凡是在src下的都是在类路径下【记住】 。src是类的根路径 /* 解析: Thread.currentThread() 当前线程对象 getContextClassLoader() 是线程对象的方法,可以获取到当前线程的类加载器对象 getResource() 【获取资源】这是类加载器对象的方法,当前线程的类加载器默认从类的根路径加载资源 ("")里写入需要加载的文件名 getPath() 获取路径的方法 */ String path = Thread.currentThread().getContextClassLoader() .getResource("classinfo2.properties").getPath();//这种方式获取文件绝对路径是通用的 //采用以上代码可以拿到一个问文件绝对路径 // /D:/Program%20Files%20(x86)/IntelliJ%20IDEA%202019.3.3/javaSE/out/production/REFLECT/classinfo2.properties System.out.println(path); //获取dp.properties文件的绝对路径(从类的根路径下作为起点开始) String path2 =Thread.currentThread().getContextClassLoader(). getResource("bean/dp.properties").getPath(); System.out.println(path2); // /D:/Program%20Files%20(x86)/IntelliJ%20IDEA%202019.3.3/javaSE/out/production/REFLECT/bean/dp.properties } }
-
获取文件路径后,传入FileReader中。如何用流的方式返回
package reflect; import java.io.File; import java.io.FileReader; import java.io.InputStream; import java.util.Properties; public class IoPropertiesText { public static void main(String[] args) throws Exception { //获取一个文件的绝对路径 //如果你们路径有空格和汉字,要在getPath()前面加个toURI() /* String path = Thread.currentThread().getContextClassLoader().getResource("classinfo2.properties").toURI().getPath(); FileReader reader = new FileReader(path);*/ //以流的方式返回 InputStream reader = Thread.currentThread().getContextClassLoader().getResourceAsStream("classinfo2.properties"); Properties pro = new Properties(); pro.load(reader); reader.close(); //通过key获取value String className = pro.getProperty("className"); System.out.println(className); } }
-
以流的形式返回文件路径
package reflect; import java.io.File; import java.io.FileReader; import java.io.InputStream; import java.util.Properties; public class IoPropertiesText { public static void main(String[] args) throws Exception { //获取一个文件的绝对路径 //如果你们路径有空格和汉字,要在getPath()前面加个toURI() /* String path = Thread.currentThread().getContextClassLoader().getResource("classinfo2.properties").toURI().getPath(); FileReader reader = new FileReader(path);*/ //以流的方式返回 InputStream reader = Thread.currentThread().getContextClassLoader().getResourceAsStream("classinfo2.properties"); System.out.println(reader); Properties pro = new Properties(); pro.load(reader); reader.close(); //通过key获取value String className = pro.getProperty("className"); System.out.println(className); } }
classinfo2.properties
className =java.util.String #bean.User
-
资源绑定器
package reflect; import java.util.ResourceBundle; /* java.util包下提供了一个资源绑定器,便于获取属性配置文件中的内容。 根据一下这种方式的时候,属性配置文件xxx.properties必须放到类路径下 */ public class ResourceBundleText { public static void main(String[] args) { //资源绑定器,只能绑定xxx.properties文件,并且这个文件必须在类路径文件下,文件扩展名也必须是properties //并且在写路径的时候,路径后面的扩展名不能写 ResourceBundle bundle = ResourceBundle.getBundle("classinfo2"); String className = bundle.getString("className"); System.out.println(className); } }
-
关于JDK中自带的类加载器:
-
什么是类加载器?
专门负责加载类的命令/工具
ClassLoader
-
JDK中自带了三个类加载器
启动类加载器(父加载器):rt.jar
扩展类加载器(母加载器):ext/*.jar
应用类加载器:classpath
-
假设有这样一段代码
String s = "abc";
代码在开始执行之前,会将所需要类全部加载到JVM当中。通过类加载器加载,看到以上代码类加载会找String.class文件,找到就加载,那么是怎么进行加载的呢?
首先通过“启动类加载器”加载。
注意:启动类加载器专门加载:C:\Program Files\Java\jdk1.8.0_221\jre\lib\rt.jar
rt.jar中都是JDK最核心的类库。
如果通过“启动类加载器”加载不到的时候,会通过“扩展类加载器”加载
注意:扩展类加载器专门加载:C:\Program Files\Java\jdk1.8.0_221\jre\lib\ext*.jar
如果“扩展类加载器”没有加载到,那么会通过“应用类加载器”加载。
注意:应用类加载器专门加载:classpath中的jar包(class文件)
-
java中为了保证类加载的安全,使用了双亲委派机制。
优先从”启动类加载器“中加载,这个称为“父”
“父”无法加载到,再从“扩展类加载器”中加载,这个称为“母”
双亲委派,如果都加载不到,才会考虑从应用类加载器中加载。直到加载到为止
-
-
反射属性
package bean; //反射属性Field public class Student { //Field翻译为字段,其实就是属性/成员 //4个Field,分别采用了不同的访问控制权限修饰符 public int no; //Field对象 private String name; protected int age; boolean sex; public static final double MATH_PI = 3.1415926; }
反射属性的方法
package reflect; import java.lang.reflect.Field; import java.lang.reflect.Modifier; /* 反射属性Field */ public class ReflectText05 { public static void main(String[] args) throws Exception{ //获取整个类 Class studentClass = Class.forName("bean.Student"); System.out.println("完整类名:"+studentClass.getName()); //bean.Student System.out.println("简类名:"+studentClass.getSimpleName()); //Student //获取类中所有的public修饰的Field Field[] fields = studentClass.getFields(); System.out.println(fields.length); //取出这个field Field f = fields[0]; //取出这个field的名字 System.out.println(f.getName()); //获取所有的field Field[] fs = studentClass.getDeclaredFields(); System.out.println(fs.length); System.out.println("---------------------------"); for(Field field : fs){ //获取属性的修饰符列表 System.out.println(field.getModifiers()); //将这个代号转换为字符串 调用Modifier.toString()方法 System.out.println(Modifier.toString(field.getModifiers())); //获取属性的类型 //System.out.println(field.getType()); //class类型的 System.out.println(field.getType().getSimpleName()); //获取简称的类名 //获取属性的名字 System.out.println(field.getName()); } } }
反编译Student类(了解)
package reflect; import java.lang.reflect.Field; import java.lang.reflect.Modifier; /* 反编译Student */ public class ReflectText06 { public static void main(String[] args) throws Exception{ //创建这个是为了拼接字符串 StringBuilder s = new StringBuilder(); Class studentClass = Class.forName("bean.Student");//bean.Student s.append(Modifier.toString(studentClass.getModifiers())+" class "+studentClass.getSimpleName()+"{\n"); Field[] fields = studentClass.getDeclaredFields(); for(Field field :fields){ s.append("\t"); s.append(Modifier.toString(field.getModifiers())); //如果有访问控制权限修饰符修饰的话,才会加:空格。如果没有则不会加 if ((field.getModifiers())!= 0){ s.append(" "); } s.append(field.getType().getSimpleName()); s.append(" "); s.append(field.getName()); s.append(";\n"); } s.append("}"); System.out.println(s); } }
-
通过反射机制访问一个java对象的属性(重点)
package reflect; import bean.Student; import java.lang.reflect.Field; /* 必须掌握: 怎么通过反射机制访问一个java对象的属性? 给属性赋值:set 获取属性的值:get */ public class ReflectText07 { public static void main(String[] args) throws Exception{ //不使用反射机制,怎么访问一个对象的属性? Student s = new Student(); //赋值 s.no = 1111; //三要素:给s对象的no属性赋值1111 //要素1:对象s //要素2:no属性 //要素3:1111 //读取属性的值 //两个要素:获取s对象no属性的值 System.out.println(s.no); //使用反射机制,怎么去访问一个对象的属性。(set、get) Class studentClass = Class.forName("bean.Student"); Object obj = studentClass.newInstance(); //obj底层就是student对象(底层调用无参构造方法) //获取no属性(根据属性的名称来获取Field) Field noField = studentClass.getDeclaredField("no"); //给obj对象(Student对象)的no属性赋值 /* 虽然使用了反射机制,但三要素还是缺一不可: 要素1:obj对象 要素2:no属性 要素3:赋值2222 注意: 反射机制虽然让代码更复杂了,但是为了“灵活”,都是值得的 */ noField.set(obj,2222); //读属性的值 //两个要素:获取obj对象的no属性的值 System.out.println(noField.get(obj)); //可以访问私有的属性吗? Field nameField = studentClass.getDeclaredField("name"); //需要打破封装,才能访问(反射机制的缺点:打破封装,可能会给不法分子留下机会!!) //调用方法打破封装后,在外部也可以访问private修饰的属性。 nameField.setAccessible(true); //赋值 nameField.set(obj,"jack"); //获取 System.out.println(nameField.get(obj)); } }
-
可变长度参数
package reflect; /* 可变长度参数: int... ages :这就是可变长度参数 语法:类型... (注意:一定是3个.) 1、可变长度参数要求的参数个数是:0~n个。 2、可变长度参数在参数列表中必须在最后一个位置上,而且可变长度参数只能有一个。 3、可变长度参数可以当作一个数组来看待 */ public class ArgsText { public static void main(String[] args) { m(); m(1); m(1,1); //编译报错 //m("abc"); s(1); s(1,"s"); s(2,"kdd","dkf"); array("ab","d","dbg","dk","kdf"); String[] strings = {"aa","bb","cc"}; //也可以传一个数组 array(strings); //直接传一个数组 array(new String[]{"我","是","中","国","人"}); //可以传数组,但没必要 array("我","是","中","国","人"); } public static void m(int... ages){ System.out.println("m方法执行了"); } //可变长度参数在参数列表中必须在最后一个位置上,而且可变长度参数只能有一个。 //public static void m2(String... names,int... ages){} //可变长度参数必须在最后 public static void s(int i,String... names){ System.out.println("ssss"); } public static void array(String... nn){ //nn有length属性,说明nn是一个数组 //可以将可变长度参数当作一个数组看待 for(int i = 0;i<nn.length;i++){ System.out.println(nn[i]); } } }
-
反编译类的方法
用户业务类
package service; /* 用户业务类 */ public class UserService { /** * 登录方法 * @param name 用户方法 * @param password 密码 * @return true表示:登陆成功 false:表示登陆失败 */ public boolean login(String name,String password){ if("admin".equals(name) && "123".equals(password)){ return true; } return false; } /** * 退出系统的方法 */ public void logout(){ System.out.println("系统已经安全退出"); } }
反射Method
package reflect; import java.lang.reflect.Method; import java.lang.reflect.Modifier; /* 了解: 反射Method */ public class ReflectText08 { public static void main(String[] args) throws Exception{ //获取类 Class userServiceClass = Class.forName("service.UserService"); //获取所有Method(包括私有的) Method[] methods =userServiceClass.getDeclaredMethods(); //遍历Method for(Method method :methods){ //获取修饰符列表 System.out.println(Modifier.toString(method.getModifiers())); //获取方法的返回值类型 System.out.println(method.getReturnType().getSimpleName()); //获取方法名 System.out.println(method.getName()); //方法的修饰符列表(一个方法的参数可能会有多个) Class[] parameterTypes =method.getParameterTypes(); for(Class c : parameterTypes){ System.out.println(c.getSimpleName()); } } } }
反编译一个类的所有方法
package reflect; import java.lang.reflect.Method; import java.lang.reflect.Modifier; /* 反编译一个类的方法 */ public class ReflectText09 { public static void main(String[] args) throws Exception{ StringBuffer s = new StringBuffer(); Class userServiceCLass = Class.forName("java.lang.String");//service.UserService s.append(Modifier.toString(userServiceCLass.getModifiers())+" class "+userServiceCLass.getSimpleName()+"{"); Method[] methods = userServiceCLass.getDeclaredMethods(); for(Method method :methods){ s.append("\n\n\t"); s.append(Modifier.toString(method.getModifiers())); if(method.getModifiers() != 0){ s.append(" "); } s.append(method.getReturnType().getSimpleName()); s.append(" "); s.append(method.getName()); s.append("("); //参数列表 Class[] parameterTypes = method.getParameterTypes(); for(Class para: parameterTypes){ s.append(para.getSimpleName()); s.append(","); } if(parameterTypes.length>1){ //删除指定下标位置上的字符 s.deleteCharAt(s.length()-1); } s.append("){}"); } s.append("\n}"); System.out.println(s); } }
-
通过反射机制调用一个对象的方法(重点)
userSrevice
package service; /* 用户业务类 */ public class UserService { /** * 登录方法 * @param name 用户方法 * @param password 密码 * @return true表示:登陆成功 false:表示登陆失败 */ public boolean login(String name,String password){ if("admin".equals(name) && "123".equals(password)){ return true; } return false; } //可能还有一个同名login方法,java中区分一个方法:依靠方法名和参数列表 public void login(int i){ } /** * 退出系统的方法 */ public void logout(){ System.out.println("系统已经安全退出"); } }
反射方法
package reflect; import service.UserService; import java.lang.reflect.Method; /* 重点:通过反射机制怎么调用一个对象的方法? 重点 反射机制,让代码具有通用性,可变化的内容都是写到配置文件当中 将来修改配置文件之后,创建的对象不一样了,调用的方法也不同了,但是java代码不需要做任何改动。这就是反射机制的魅力 */ public class ReflectText10 { public static void main(String[] args) throws Exception{ //不使用反射机制,怎么调用方法? UserService userService = new UserService(); /* 调用方法的四要素分析: 1、对象userService 2、login方法名 3、实参列表 4、返回值 */ boolean loginSuccess = userService.login("admin","123"); System.out.println(loginSuccess?"登陆成功":"登陆失败"); //使用反射机制来调用一个对象的方法应该怎么做? Class userServiceClass = Class.forName("service.UserService"); //创建对象 Object obj = userServiceClass.newInstance(); //获取Method Method loginMethod = userServiceClass.getDeclaredMethod("login", String.class, String.class); //调用方法 也需要四要素。 //反射机制中最重要的一个方法,必须记住 // 返回值 方法 对象 实参列表 Object retValue = loginMethod.invoke(obj,"admin","123"); //loginMethod方法、obj对象、实参“admin”“123”、retValue返回值 System.out.println(retValue); } }
-
反编译一个类的构造方法
Vip类
package bean; public class Vip { int no; String name; String birth; boolean sex; public Vip(int no, String name, String birth, boolean sex) { this.no = no; this.name = name; this.birth = birth; this.sex = sex; } public Vip(int no, String name, String birth) { this.no = no; this.name = name; this.birth = birth; } public Vip(int no, String name) { this.no = no; this.name = name; } public Vip() { } public Vip(int no) { this.no = no; } }
反编译一个类的构造方法
package reflect; /* 反编译一个类的Constructor构造方法 */ import java.lang.reflect.Constructor; import java.lang.reflect.Modifier; public class ReflectText11 { public static void main(String[] args) throws Exception{ StringBuilder s = new StringBuilder(); Class vipClass = Class.forName("java.lang.String");//bean.Vip s.append(Modifier.toString(vipClass.getModifiers())); s.append(" Class "); s.append(vipClass.getSimpleName()); s.append("{"); //拼接构造方法 Constructor[] constructors = vipClass.getDeclaredConstructors(); for(Constructor constructor :constructors){ s.append("\n\n\t"); s.append(Modifier.toString(constructor.getModifiers())); s.append(" "); s.append(vipClass.getSimpleName()); s.append("("); //拼接参数 Class[] parameterTypes = constructor.getParameterTypes(); for(Class parameterType :parameterTypes){ s.append(parameterType.getSimpleName()); s.append(","); } //如果有参数,则删掉最后的,号,如果没有参数,则不删除 if(parameterTypes.length >0){ s.deleteCharAt(s.length()-1); } s.append("){}"); } s.append("}"); System.out.println(s); } }
-
通过反射机制调用构造方法实例化java对象
vip类
package bean; public class Vip { int no; String name; String birth; boolean sex; public Vip(int no, String name, String birth, boolean sex) { this.no = no; this.name = name; this.birth = birth; this.sex = sex; } public Vip(int no, String name, String birth) { this.no = no; this.name = name; this.birth = birth; } public Vip(int no, String name) { this.no = no; this.name = name; } public Vip() { } public Vip(int no) { this.no = no; } @Override public String toString() { return "Vip{" + "no=" + no + ", name='" + name + '\'' + ", birth='" + birth + '\'' + ", sex=" + sex + '}'; } }
实例化
package reflect; import bean.Vip; import java.lang.reflect.Constructor; /* 比上一个例子(11)重要一些 通过反射机制调用构造方法实例化java对象 */ public class ReflectText12 { public static void main(String[] args) throws Exception{ //不使用反射机制创建对象 Vip v1 = new Vip(); Vip v2 = new Vip(100,"zhangsan","2001-10-11",true); //使用反射机制创建对象 Class vipClass = Class.forName("bean.Vip"); //调用无参数的构造方法 Object obj =vipClass.newInstance(); System.out.println(obj); //调用有参数的构造方法怎么办? //第一步:先获取到这个有参数的构造方法 Constructor con = vipClass.getDeclaredConstructor(int.class,String.class,String.class,boolean.class); //第二步:调用构造方法new对象 Object newObj = con.newInstance(200,"lisi","1999-09-09",true); System.out.println(newObj); //获取无参数的构造方法 Constructor con2 = vipClass.getDeclaredConstructor(); Object newObj2 = con2.newInstance(); System.out.println(newObj2); } }
-
重点:怎么获取一个类的父类/实现的接口
package reflect; /* 重点:怎么获取一个类的父类,以及实现的接口 */ public class ReflectText13 { public static void main(String[] args) throws Exception{ //String类举例 Class stringClass =Class.forName("java.lang.String"); //获取String类的父类 Class superClass =stringClass.getSuperclass(); System.out.println(superClass.getName()); //获取String类实现的所有接口(一个类可以实现多个接口) Class[] interfaces = stringClass.getInterfaces(); for(Class face :interfaces){ System.out.println(face.getName()); } } }