导图社区 反射
Java反射机制,应用于编译之后的执行阶段,获取类类对象,即Class对象。从而获得类相关的实例属性和方法,进一步操作类。
编辑于2020-08-05 19:20:20Java是一门面向对象的编程语言,不仅吸收了C++语言的各种优点,还摒弃了C++里难以理解的多继承、指针等概念,因此Java语言具有功能强大和简单易用两个特征。Java语言作为静态面向对象编程语言的代表,极好地实现了面向对象理论,允许程序员以优雅的思维方式进行复杂的编程
用多线程只有一个目的,那就是更好的利用cpu的资源,因为所有的多线程代码都可以用单线程来实现。说这个话其实只有一半对,因为反应“多角色”的程序代码,最起码每个角色要给他一个线程吧,否则连实际场景都无法模拟,当然也没法说能用单线程来实现:比如最常见的“生产者,消费者模型”。
平和保存和搜索的一些好用的网站,分享一波,好用拿走。
社区模板帮助中心,点此进入>>
Java是一门面向对象的编程语言,不仅吸收了C++语言的各种优点,还摒弃了C++里难以理解的多继承、指针等概念,因此Java语言具有功能强大和简单易用两个特征。Java语言作为静态面向对象编程语言的代表,极好地实现了面向对象理论,允许程序员以优雅的思维方式进行复杂的编程
用多线程只有一个目的,那就是更好的利用cpu的资源,因为所有的多线程代码都可以用单线程来实现。说这个话其实只有一半对,因为反应“多角色”的程序代码,最起码每个角色要给他一个线程吧,否则连实际场景都无法模拟,当然也没法说能用单线程来实现:比如最常见的“生产者,消费者模型”。
平和保存和搜索的一些好用的网站,分享一波,好用拿走。
反射
定义
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性(包括私有及其他);这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。
所谓的反射其实是获取类的字节码文件,也就是.class 文件。
java文件的源码是.java的文件,编译之后就是.class的二进制字节码文件。在java中,一切事务皆对象,这些编译后的class文件也被当做对象,被抽象为一种类,就是Class。
反射的操作
反射所处Java运行位置:
源文件.java(编译)--.class(运行--反射)--执行
动态加载数据
反射操作-
1.获取类的Class对象(即.class文件)//所有的操作都基于类的实例(区别于对象的实例)
2.根据类对象操作与类相关的一些成员或者父类/接口
类加载机制
类加载器把类装进虚拟机需要:装载、链接和初始化。
装载:查找和导入类或接口的二进制数据;
链接:执行下面的校验、准备和解析步骤(解析步骤可选)
校验:检查导入类或接口的二进制数据的正确性;
准备:给类的静态变量分配并初始化存储空间;
解析:将符号引用转成直接引用;
初始化:激活类的静态变量的初始化 Java 代码和静态 Java 代码块。
类加载
类加载器
系统类加载器
自定义类加载器
双亲委托机制介绍
首先判断class是否已经加载
如果没有则不是自身去查找而是托给父加载器迸行查找,这样依次的进行递归,直到委托到最顶层的 Bootstrap ClassLoader
如果 Bootstrap Classloader找到了该Cass,就会直接返回
如果没找到,则继续依次向下查找,如果还没找到则最后会交由自身去查找
意义
+ 通过反射机制可以让程序创建和控制任何类的对象,无需提前硬编码目标类。
+ 使用反射机制能够在运行时构造一个类的对象、判断一个类所具有的成员变量和方法、调用一个对象的方法。
+ 反射机制是构建框架技术的基础所在,使用反射可以避免将代码写死在框架中。
反射机制极大的提高了程序的灵活性和扩展性,降低模块的耦合性,提高自身的适应能力。
反射优缺点
优点:运行期类型的判断,动态加载类,提高代码灵活度。
缺点:反射机制的功能非常强大,但不能滥用。在能不使用反射完成时,尽量不要使用。
- 性能问题 : 反射操作的效率要比正常操作效率低很多
- 安全限制 : 使用反射通常需要程序的运行没有安全方面的限制。如果一个程序对安全性提出要求,则最好不要使用反射
- 程序健壮性 : 反射允许代码执行一些通常不被允许的操作,所以使用反射有可能会导致意想不到的后果
静态编译:在编译时确定类型,绑定对象。
动态编译:运行时确定类型,绑定对象。
相关类
Class类代表类的实体,在运行的Java应用程序中表示类和接口
Field类代表类的成员变量(成员变量也称为类的属性)
Method类代表类的方法
Constructor类代表类的构造方法
反射的三种方式
对象名.getClass()
类名.class方式
类的全限定名获取Class对象
反射所涉及到的类
class
实例基类 //接口类 package fanshe; public interface Interface1 {//接口1 void interface1(); } package fanshe; public interface Interface2 {//接口2 void interface2(); } ---------------------------------------------- //基本类 package fanshe; public class Person implements Interface1,Interface2{//继承接口 private int age; private String name; private int id;//私有属性 @Override//重写接口中的方法 public void interface1() { System.out.println("Interface Method....."); } @Override public void interface2() { System.out.println("This is a Interface02 Method......"); } public static void staticMethod() { //定义静态方法 System.out.println("This is Static Method......"); } public int getAge() {//私有属性的get/set方法 return age; } public void setAge(int age) { this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getId() { return id; } public void setId(int id) { this.id = id; } public Person(int age, String name, int id) {//不同参数的公有构造方法 this.age = age; this.name = name; this.id = id; } public Person(int id) { this.id = id; } public Person() { } private Person(String name) {//私有构造方法 this.name = name; } private void privateMethod() {//私有方法 System.out.println("This is a privateMethid..."); } private void privateMethod2(String name) { System.out.println("privateMethod...."+name); } } -------------------------------------------- 需要引入的包: package fanshe;//通过反射获取类对象/类实例 import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.ArrayList; public class ReflectDemo01 {} //tips: 在这里需要注意的是,如果操作私有的方法或者属性, //必须要暂时放开权限 才可以对私有的变量进行赋值 或者调用私有的方法: 属性/方法.setAccessible(true);
与类相关的方法
获取一个对象的父类和实现的接口: public void demo2() throws ClassNotFoundException { Class perClass = Class.forName("fanshe.Person"); //获取父类 Class parentClass = perClass.getSuperclass(); System.out.println("父类为:"+parentClass.getName()); //获取所有接口 Class interf[] = perClass.getInterfaces(); System.out.println("实现的接口有:"); for(int i = 0; i System.out.println((i+1)+":"+interf[i].getName()); } } //输出结果 父类为:class java.lang.Object 实现的接口有: 1:fanshe.Interface1 2:fanshe.Interface2 --------------------------------------------- 创建对象实例: //获取类对象实例,操作对象属性和方法 public static void demo01() throws Exception{ Class preClass = Class.forName("fanshe.Preson"); //Person p = (Person)preClass.newInstance(); Object object = preClass.newInstance(); Person p = (Person)object; p.setName("skh"); p.setAge(18); p.setId(1); System.out.println(p.getN ame()); }
getClassLoader()
获得类的加载器
getClass()
返回一个数组,数组中包含该类中所有类和接口类的对象
getSimpleName()
获得类名
getInterfaces()
获取当前类实现的类或接口
getSuperclass()
获得当前类继承的父类的名字
forName(String className)
根据类名返回类的对象
getPackage()
获得类的包
newInstance()
创建类的实例
与属性相关的方法
获取某个类的全部属性: public void demo04() throws Exception { //1.获取Class对象 Classss pclass = getClass().forName("fanshe.Person"); //2.获取所有公有字段 System.out.println("获取所有公有字段"); Field[] fieldArray = pclass.getFields(); for(Field f : fieldArray) { System.out.println(f); } //3.获取所有字段 //所有属性 (属性的 :公共属性\所有属性的区别 同“方法”) System.out.println("----------------------------"); System.out.println("获取所有字段"); fieldArray = pclass.getDeclaredFields(); for(Field f : fieldArray) { System.out.println(f); } System.out.println("----------------------------"); Field f = pclass.getField("name"); System.out.println(f); //获取一个对象 Object obj = pclass.getConstructor().newInstance(); f.set(obj,"张三"); //验证 Person p = (Person)obj; System.out.println("验证的姓名:"+p.getName()); System.out.println("----------------------------"); f = pclass.getDeclaredField("age"); System.out.println(f); f.setAccessible(true); f.set(obj, 21); System.out.println("验证年龄:"+p); } //输出结果 获取所有公有字段 ---------------------------- 获取所有字段 private int fanshe.Person.age private java.lang.String fanshe.Person.name private int fanshe.Person.id
getField(String name)
获得某个公有的属性对象
getFields()
获得所有公有的属性对象
getDeclaredField(String name)
获得某个的属性对象
getDeclaredFields()
获得所有的属性对象
构造器相关的方法
获取某个类的全部构造函数并调用私有构造方法: public void demo03() throws Exception { //1.加载Class对象 Classss Class = Class.forName("fanshe.Person"); //2.获取所有公有构造方法 System.out.println("所有公有构造方法"); Constructor[] conArray = Class.getConstructors(); for(Constructor c : conArray) { System.out.println(c); } //3.获取所有的构造方法 System.out.println("----------------------------"); System.out.println("所有构造方法"); conArray = Class.getDeclaredConstructors(); for(Constructor c : conArray) { System.out.println(c); } //4.无参构造的话就不需要了反射入口 System.out.println("----------------------------"); System.out.println("公有、无参的构造方法"); Constructor con = Class.getConstructor(null); System.out.println("con = " + con); //调用构造方法创建对象实例 Object obj = con.newInstance(); System.out.println("obj = " + obj); System.out.println("----------------------------"); System.out.println("获取私有的构造方法,并调用"); con = Class.getDeclaredConstructor(int.class); System.out.println(con); //调用私有构造方法,创建对象实例 con.setAccessible(true); obj = con.newInstance(20); } //输出结果 所有公有构造方法 public fanshe.Person(int,java.lang.String,int) public fanshe.Person(int) public fanshe.Person() ---------------------------- 所有构造方法 public fanshe.Person(int,java.lang.String,int) public fanshe.Person(int) public fanshe.Person() private fanshe.Person(java.lang.String) ---------------------------- 公有、无参的构造方法 con = public fanshe.Person() obj = fanshe.Person@2a139a55 ---------------------------- 获取私有的构造方法,并调用 public fanshe.Person(int)
getConstructor(Class...<?>parameterTypes)
获得该类中参数类型匹配的构造方法
getConstructosr()
获得该类中所有的构造方法
getDeclaredConstructor(Class...<?>parameterTypes)
获得该类中参数类型匹配的构造方法(公有的)
getDeclaredConstructors()
获得该类中所有的构造方法(公有的)
与注解相关的方法
getAnnotation( Class<A> annotation class)
返回与参数类型匹配的注解对象
getAnnotationst()
返回该类所有的公有注解对象
getDeclaredAnnotation( Class<A> annotationclass)
返回该类中与参数类型匹配的所有注解对象
isAnnotationo()
如果是注解类型则返回true
getDeclaredAnnotations0
返回该类所有的注解对象
isAnnotation Present(Class<? extends Annotation> annotationClass)
如果是指定类型注解类型则返回true
与方法有关的方法
获取某个类的全部方法: public void demo05() throws Exception { //1.获取Class对象 Class pClass = Class.forName("fanshe.Person"); //2.获取所有公有方法 ////获取所有的 公共的方法(1.本类以及父类、接口中的所有方法 2.符合访问修饰符规律) System.out.println("获取所有的公有方法*"); Method[] methodArray = pClass.getMethods(); for(Method m : methodArray){ System.out.println(m); } //3.获取所有的方法 //获取当前类的所有方法(1.只能是当前类 2.忽略访问修饰符限制) System.out.println("----------------------------"); System.out.println("获取所有的方法"); methodArray = pClass.getDeclaredMethods(); for(Method m : methodArray){ System.out.println(m); } //4.操作 System.out.println("获取公有的test1()方法"); Method m = pClass.getMethod("test1", String.class); System.out.println(m); //实例化一个Student对象 Object obj = pClass.getConstructor().newInstance(); m.invoke(obj, "lisi"); System.out.println("获取私有的test4()方法*"); m = pClass.getDeclaredMethod("test4", int.class); System.out.println(m); m.setAccessible(true);//解除私有限定 Object result = m.invoke(obj, 20);//需要两个参数,一个是要调用的对象(获取有反射),一个是实参 System.out.println("返回值:" + result); } //输出结果 -------------------------------------------
getMethod(String name,dass.<?> parameterTypes)
获得该类某个公有的方法
getMethods()
获得该类所有公有的方法
getDeclaredMethod( String name ,Class...<?> parameterTypes)
获得该类某个方法
getDeclaredMethods()
获得该类所有方法
Method
invoke(Object obj,Object...args)
方法反射:invoke: public void test7() throws Exception{ Class Class = Clsss.forName("DateTest.ReflectTest"); Method method = class.getMethod("show1"); method.invoke(class.newInstance()); method = class.getMethod("show2", int.class, String.class); method.invoke(Class.newInstance(), 20, "张三"); } public void show1() { System.out.println("调用show1()"); } public void show2(int age, String name) { System.out.println("调用show2()"); System.out.println("age: " + age + "name: " + name); } //输出结果 Method对象的invoke方法来委托动态构造的对应类型对象,使其执行对应形参的add方法,这是回调函数(方法)的功能//method.invoke(list,"zs...");System.out.println(list);--list
调用/传递object对象及参数,调用该对象对应的方法
私有的属性需要[method/Field/constructor].setAccessible(true);开放权限。
反射的应用
反射的动态代理
首先需要定义一个 InvocationHandler 接口的子类,完成代理的具体操作
package DateTest; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class MyInvocationHandler implements InvocationHandler{ private Object obj = null; public Object bind(Object obj) { this.obj = obj; return Proxy.newProxyInstance(obj.getClassss().getClassssLoader(), obj.getClassss().getInterfaces(), this); } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object temp = method.invoke(this.obj, args); return temp; } } package DateTest; public interface Animal { public void eat(String name); } package DateTest; public class Dog implements Animal{ public void eat(String name) { System.out.println(name+"eat!"); } }
新建类实现接口Animal,使用 MyInvocationHandler.bind() 方法获取即可实现调用。
public void test8(){ MyInvocationHandler invo = new MyInvocationHandler(); Animal sub = (Animal) invo.bind(new Dog()); sub.eat("小狗"); } //输出结果 小狗eat!
反射越过泛型检查
泛型作用在编译期,编译过后泛型擦除(消失掉)。所以是可以通过反射越过泛型检查的。//虽然可以通过反射 访问private等访问修饰符不允许访问的属性/方法,也可以忽略掉泛型的约束;但实际开发 不建议这样使用,因此可能造成程序的混乱。 ------------------------------------- public void test9() throws Exception{ ArrayList list = new ArrayList(); list.add(111); list.add(222); Method method = list.getClass().getMethod("add", Object.class); method.invoke(list, "越过泛型检查"); //遍历集合 for(Object obj : list){ System.out.println(obj); } }--泛型为Integer类型,输出为字符串 //输出结果 111 222 越过泛型检查
获取数组信息并改变数组大小和值
通过反射机制分别修改int和String类型的数组的大小并修改int数组的第一个值。 ------- public static void test10() throws Exception{ int[] temp = { 12,45,65,5,1,32,4,56,12}; int[] newTemp = (int[]) arrayInc(temp, 15); print(newTemp); Array.set(newTemp, 0, 100); System.out.println("修改之后数组第一个元素为: " + Array.get(newTemp, 0)); print(newTemp); String[] atr = { "a", "b", "c" }; String[] str1 = (String[]) arrayInc(atr, 8); print(str1); } // 修改数组大小 public static Object arrayInc(Object obj, int len) { Class arr = obj.getClass().getComponentType(); Object newArr = Array.newInstance(arr, len); int co = Array.getLength(obj); System.arraycopy(obj, 0, newArr, 0, co); return newArr; } // 打印 public static void print(Object obj) { Classss c = obj.getClass(); if (!c.isArray()) { return; } Classss arr = obj.getClass().getComponentType(); System.out.println("数组类型: " + arr.getName()); System.out.println("数组长度为: " + Array.getLength(obj)); for (int i = 0; i System.out.print(Array.get(obj, i) + " "); } System.out.println(); } //输出结果 数组类型: int 数组长度为: 15 12 45 65 5 1 32 4 56 12 0 0 0 0 0 0 修改之后数组第一个元素为: 100 数组类型: int 数组长度为: 15 100 45 65 5 1 32 4 56 12 0 0 0 0 0 0 数组类型: java.lang.String 数组长度为: 8 a b c null null null null null
应用于工厂模式
对于普通的工厂模式当我们在添加一个子类的时候,就需要对应的修改工厂类。 当我们添加很多的子类的时候,会很麻烦。 ---- package DateTest; public interface Animal { public abstract void sleep(); }//定义接口 package DateTest; public class Dog implements Animal{ @Override public void sleep() { System.out.println("dog"); } }//实现dog package DateTest; public class Cat implements Animal{ @Override public void sleep() { System.out.println("cat"); } }//实现cat package DateTest; public class Factory {//定义工厂 public static Animal getInstance(String ClassName) { Animal a = null; try { a = (Animal) Class.forName(ClassName).newInstance();//通过反射获取实例 } catch (Exception e) { e.printStackTrace(); } return a; }//返回一个实例 } public void test11()throws Exception{ Animal a = Factory.getInstance("DateTest.Dog"); if (a != null) { a.sleep(); }//获取实例,执行对应实例的方法 } //输出结果 dog
加载配置文件
//配置文件内容为 //className = DateTest.ReflectTest public void test13() throws IOException { Properties pro = new Properties();//获取配置文件的对象 InputStream in=new Person().getClass().getResourceAsStream("/pro.txt"); pro.load(in);//将流加载到配置文件对象中 in.close(); System.out.println(pro.getProperty("className")); } //输出结果 默认的无参构造方法执行了 DateTest.ReflectTest
获取ClassLoader类加载器
public void test12() throws ClassNotFoundException { //1、获取一个系统的类加载器 ClassLoader classLoader = ClassLoader.getSystemClassLoader(); System.out.println(classLoader); //2、获取系统类加载器的父类加载器 classLoader = classLoader.getParent(); System.out.println(classLoader); //3、获取扩展类加载器的父类加载器 //输出为Null,引导类加载器无法被Java程序直接引用 classLoader = classLoader.getParent(); System.out.println(classLoader); //4、测试当前类由哪个类加载器进行加载 ,结果是系统的类加载器 classLoader = Class.forName("DateTest.ReflectTest").getClassLoader(); System.out.println(classLoader); //5、测试JDK提供的Object类由哪个类加载器负责加载的 classLoader = Class.forName("java.lang.Object").getClassLoader(); System.out.println(classLoader); } //输出结果 sun.misc.Launcher$AppClassLoader@6bc7c054 sun.misc.Launcher$ExtClassLoader@2ef1e4fa null sun.misc.Launcher$AppClassLoader@6bc7c054 null
Class类
Class类的实例表示正在运行的Java应用程序中的类和接口。JVM中有N多的实例,每个类的实例都有Class对象。(包括基本数据类型)。
Class类没有公共构造方法。Class类对象是在加载类时由Java虚拟机(JVM)以及通过调用类加载器(ClassLoader)中的 defineClass方法自动构造的。也就是这不需要我们自己去处理创建,JVM 已经帮我们创建好了。
反射的三种入口
//通过反射获取类实例名(类对象名)(反射入口-3) //demo1()获取反射对象Class: 1.Class.forName(全类名) 2.类名.class 3.对象.getClass() public static void demo1() throws Exception { //1 Class.forName("全类名") Class perClass1 = Class.forName("fanshe.Person"); System.out.println(perClass1.getName()); //2 类名.class//同包情况下可以使用 Class perClass2 = Person.class ; System.out.println(perClass2.getName()); //3 对象.getClass() Person per = new Person(); Class perClass3 = per.getClass(); System.out.println(perClass3.getName()); } //输出结果 fanshe.Person fanshe.Person fanshe.Person
1.通过Object类的getClass方法获取//对象.getClass-->list.getClass
java.lang.Object中定义有getClass方法:public final Class getClass()。所有Java对象都具备这个方法,该方法用于返回调用该方法的对象的所属类关联的Class对象
Person p = new Person(); Class class1 = p.getClass();
2. 使用.class的方式//类名.class-->person.class
任何数据类型(包括基本数据类型)都有一个“静态”的 class属性,使用类名加“.class”的方式即会返回与该类对应的Class对象。
任何数据类型(包括基本数据类型)都有一个“静态”的 class属性,使用类名加“.class”的方式即会返回与该类对应的Class对象。
Class class = String.class;
3. 使用Class.forName方法(ClassNotFoundException)//包名.类名
Class<?> class1 = Class.forName("org.whatisjava.reflect.Foo");