反射

反射(Reflection)

类加载后,在堆内存中的方法区中间产生了一个Class类型的对象(一个类只有一个Class对象),这个对象就包含了完整的类的结构信息。我们可以通过这个对象看到类的结构。这个对象就像一面镜子,透过这个镜子看到类的结构,所以,称之为:反射

反射机制允许程序在运行期间获取任何类的内部信息(比如类名,类的接口,类的方法,字段,属性…),并且能够直接操作任意对象的内部属性及方法

反射提供的功能

  • 在运行时判断任意一个对象所属的类
  • 在运行时构造任意一个类的对象
  • 在运行时判断任意一个类所具有的成员变量和方法
  • 在运行时获取泛型信息
  • 在运行时调用任意一个对象的成员变量和方法
  • 在运行时处理注解
  • 生成动态代理

反射的优缺点

优点:

可以实现动态创建对象和编译,体现出很大的灵活性

缺点:

对性能有影响,使用反射基本上是一种解释操作,我们可以告诉JVM 我们希望做什么并且它满足我们的需求。这类操作总是慢于 直接执行相同的操作。

1.Class类

在这里插入图片描述

通过对象反射出类的名称就是反射
在这里插入图片描述

Class类的常用方法

在这里插入图片描述

获得Class类的方式

a)如果已有具体的类,通过类的class属性获取,最为安 全可靠且性能最高的方法。

Class 变量=XXX.class;

b)已知某个类的实例,调用此实例的getClass()方法获取Class对象。

Class 变量=XXX.getClass();

c)已知一个类的全名且在类路径下,可以通过Class类的静态方法forName()获取,需要处理异常ClassNotFoundException

Class 变量=Class.forName("类的全限定名");

d)内置基本数据类型可以直接使用类名.Type

e)还可以用ClassLoader(之后讲解)

测试:

public class TestClass {
	
	public static void main(String[] args) throws ClassNotFoundException {
		
		Student student=new Student();
		
		//a.
		Class class1=Student.class;
		
		//b.
		Class class2=student.getClass();
		
		//c.
		Class class3=Class.forName("com.kuang.reflect.Student");
		
		//d.
		Class class4=Integer.TYPE;
		
		//获得父类Class
		Class class5=class1.getSuperclass();
		
		System.out.println(class1.hashCode());
		System.out.println(class2.hashCode());
		System.out.println(class3.hashCode());
		System.out.println(class4);
		System.out.println(class5);
		
	}	
}

class Person{}

class Student extends Person{}

在这里插入图片描述
哪些类型可以有Class对象?

  • class:外部类、成员(成员内部类、静态内部类),局 - 部内部类,匿名内部类。
  • interface:接口
  • []:数组
  • enum:枚举
  • annotation:注解 (也是一个类型)
  • primitive type:基本数据类型
  • void

测试:

public class Test {
	
	public static void main(String[] args) {
		
		Class class1=Object.class;//类
		Class class2=Serializable.class;//接口
		Class class3=String[].class;//一维数组
		Class class4=int[][].class;//二维数组
		Class class5=Override.class;//注解
		Class class6=ElementType.class;//枚举
		Class class7=double.class;//基本数据类型
		Class class8=void.class;//void (代表空类型)
		Class class9=Class.class;//Class
		
		System.out.println(class1);
		System.out.println(class2);
		System.out.println(class3);
		System.out.println(class4);
		System.out.println(class5);
		System.out.println(class6);
		System.out.println(class7);
		System.out.println(class8);
		System.out.println(class9);
	}
	
}

在这里插入图片描述

2.类加载内存分析

java内存分析

在这里插入图片描述
类的加载过程
在这里插入图片描述
在这里插入图片描述
测试:

public class Test05 {
    public static void main(String[] args) {
        A a =new A();
        System.out.println(A.m);

        /**
         * 1.加载到内存,会产生一个对应Class对象
         * 2.链接, 链接结束后 m=0
         * 3.初始化
                <clint>(){
                        System.out.println("A类静态代码块初始化");
                        m=300;
                        m=100;
                 }
         */
    }
}

class A{

    //无参构造方法
    public A(){
        System.out.println("A类的无参构造初始化");

    }
    //静态代码块
    static {
        System.out.println("A类静态代码块初始化");
        m=300;
    }

    /**
     * m=300
     * m=100  (覆盖了上面的值)
     */
    //静态变量
    static int m=100;
}

在这里插入图片描述

1.加载阶段,将编译好的class文件加载到内存中(方法区)
2.然后会生成一个代表这个类的Class对象
3.链接阶段,会为静态变量分配内存并设置默认值
4.开始执行main方法,实例化一个A对象放在堆内存中,然后去找自己的Class类(图上4有误),拿到所有属性,方法等
5.初始化阶段,赋值
在这里插入图片描述
分析类的初始化
在这里插入图片描述
1.主动引用

public class Test06 {

    static {
        System.out.println("Main类被加载");
    }
    public static void main(String[] args) throws ClassNotFoundException {
        //1.主动引用
        // Son son=new Son();

        //反射也会产生主动引用
        Class.forName("com.javacto.reflection.Son");
    }
}
class Father{
    static int b=2;

    static {
        System.out.println("父类被加载");
    }
}

class Son extends Father{
    static {
        System.out.println("子类被加载");

    }
    static  int m=100;
    static final int M =1;  //常量
}

在这里插入图片描述
2.被动引用

public class Test06 {


    static {
        System.out.println("Main类被加载");
    }
    public static void main(String[] args) throws ClassNotFoundException {


        //System.out.println("==========不会发生类的初始化===============");
        //不会产生类的引用的方法
        //1.通过子类引用父类的静态变量,不会导致子类初始化  因为static在链接阶段的时候已经存在了
        //System.out.println(Father.b);  

        //2.只是一个数组 命了一个名和开辟了空间而已
        //Son [] array= new Son[5];
		//System.out.println(array.getClass());
		
        //3.常量不会引起父类与子类的初始化,因为所有的常量以及静态变量,都是在链接阶段都已经赋了一个值了,初始化的时候已经存在了
        //System.out.println(Son.M);  


    }
}
class Father{
    static int b=2;

    static {
        System.out.println("父类被加载");
    }
}

class Son extends Father{
    static {
        System.out.println("子类被加载");

    }
    static  int m=100;
    static final int M =1;  //常量
}

在这里插入图片描述

3.类加载器

在这里插入图片描述
在这里插入图片描述
java平台核心库:rt.jar 包

扩展类加载器 :ExtClassLoader:

系统类加载器: System Classloder ,也有的地方叫 AppClassLoader

java的运行环境jre -> lib 目录 运行所有的jar包都在这里面

测试获取加载器:

public class Test07 {
    public static void main(String[] args) throws ClassNotFoundException {
        //获取系统的类加载器
        ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
        System.out.println("系统类加载器:"+systemClassLoader);

        //获取系统类加载的父类加载器--> 扩展类加载器
        ClassLoader parent = systemClassLoader.getParent();
        System.out.println("扩展类加载器:"+parent);

        //获取扩展类加载器的父类加载器--> 根加载器 (c/c++ 写的 读取不到 返回 null)
        ClassLoader parent1 = parent.getParent();
        System.out.println("根加载器:"+parent1);

        //测试当前类是哪个加载器加载的
        ClassLoader classLoader = Class.forName("com.javacto.reflection.Test07").getClassLoader();
        System.out.println("当前类加载器:"+classLoader);

        //测试jdk 内置的类是谁加载的
        classLoader=Class.forName("java.lang.Object").getClassLoader();
        System.out.println("JDK内置类的加载器:"+classLoader);  //根加载器加载的

    }
}

可以看到 jdk内置类的加载器 就是java平台核心库,跟加载器加载的 因为用c++编写的的,无法直接获取 所有显示null。

了解什么是双亲委派机制:

如果一个类加载器收到了类加载请求,它并不会自己先去加载,而是把这个请求委托给父类的加载器去执行,如果父类加载器还存在其父类加载器,则进一步向上委托,依次递归,请求最终将到达顶层的启动类加载器,如果父类加载器可以完成类加载任务,就成功返回,倘若父类加载器无法完成此加载任务,子加载器才会尝试自己去加载,这就是双亲委派模式,即每个儿子都很懒,每次有活就丢给父亲去干,直到父亲说这件事我也干不了时,儿子自己才想办法去完成。

优点:避免重复加载 + 避免核心类篡改

采用双亲委派模式的是好处是Java类随着它的类加载器一起具备了一种带有优先级的层次关系,通过这种层级关可以避免类的重复加载,当父亲已经加载了该类时,就没有必要子ClassLoader再加载一次。其次是考虑到安全因素,java核心api中定义类型不会被随意替换,假设通过网络传递一个名为java.lang.Integer的类,通过双亲委托模式传递到启动类加载器,而启动类加载器在核心Java API发现这个名字的类,发现该类已被加载,并不会重新加载网络传递的过来的java.lang.Integer,而直接返回已加载过的Integer.class,这样便可以防止核心API库被随意篡改。

4.获取运行时类的完整结构

Field(字段)、Method(方法)、Constructor(构造器)、Superclass(父类)、Interface(接口)、Annotation(注解)

//获得类的信息
public class Test08 {

    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException {
        Class c1 = Class.forName("com.javacto.reflection.User");

        System.out.println("=========获得类的名字=============");
        //类的名字
        System.out.println(c1.getName());//包名+类名
        System.out.println(c1.getSimpleName());//类名

        /*
        打印输出结果
        com.javacto.reflection.User
        User
         */

        System.out.println("==============获得类的属性===================");
        //获得类的属性
        Field[] field1=c1.getFields();//只能找到public属性
        for (Field f :field1) {
            System.out.println(f);  //因为没有定义public属性所以为无
        }

        Field[] field2=c1.getDeclaredFields();//找到全部的属性
        for (Field f :field2) {
            System.out.println(f);
        }

        /*
        输出结果
        private java.lang.String com.javacto.reflection.User.name
        private int com.javacto.reflection.User.id
        private int com.javacto.reflection.User.age
         */

        //获得指定的属性
        Field name=c1.getDeclaredField("name");
        System.out.println("指定:"+name);

        /*
        输出结果
        指定:private java.lang.String com.javacto.reflection.User.name
         */

        //获得类的方法
        System.out.println("==============获得类的方法===================");
        Method[] methods=c1.getMethods();//获得本类及其父类的全部public方法
        for (Method method:methods) {
            System.out.println("正常的:"+method);
        }
        methods=c1.getDeclaredMethods();//获得本类的所有方法,包括私有的  不包括父类
        for (Method method:methods) {
            System.out.println("DeclaredMethods:"+method);
        }

        /*
        打印输出结果
        正常的:public java.lang.String com.javacto.reflection.User.toString()
        正常的:public java.lang.String com.javacto.reflection.User.getName()
        正常的:public int com.javacto.reflection.User.getId()
        正常的:public void com.javacto.reflection.User.setName(java.lang.String)
        正常的:public int com.javacto.reflection.User.getAge()
        正常的:public void com.javacto.reflection.User.setId(int)
        正常的:public void com.javacto.reflection.User.setAge(int)
        正常的:public final void java.lang.Object.wait() throws java.lang.InterruptedException
        正常的:public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
        正常的:public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
        正常的:public boolean java.lang.Object.equals(java.lang.Object)
        正常的:public native int java.lang.Object.hashCode()
        正常的:public final native java.lang.Class java.lang.Object.getClass()
        正常的:public final native void java.lang.Object.notify()
        正常的:public final native void java.lang.Object.notifyAll()
        DeclaredMethods:public java.lang.String com.javacto.reflection.User.toString()
        DeclaredMethods:public java.lang.String com.javacto.reflection.User.getName()
        DeclaredMethods:public int com.javacto.reflection.User.getId()
        DeclaredMethods:public void com.javacto.reflection.User.setName(java.lang.String)
        DeclaredMethods:private void com.javacto.reflection.User.test()
        DeclaredMethods:public int com.javacto.reflection.User.getAge()
        DeclaredMethods:public void com.javacto.reflection.User.setId(int)
        DeclaredMethods:public void com.javacto.reflection.User.setAge(int)
         */

        //获得指定方法只需要在()中添加参数(方法名,方法参数)
        //添加参数  实则是因为考虑到了 重载
        System.out.println("============获得指定方法=============");
        Method getName = c1.getMethod("getName", null);
        Method setName = c1.getMethod("setName", String.class);

        System.out.println(getName);
        System.out.println(setName);

        /*
        打印输出结果
        public java.lang.String com.javacto.reflection.User.getName()
        public void com.javacto.reflection.User.setName(java.lang.String)
         */


        System.out.println("=============获得指定的构造器===================");
        //获得指定构造器
        Constructor[] constructors=c1.getConstructors();//获得public方法

        for (Constructor constructor : constructors) {
            System.out.println("public:"+constructor);
        }
        constructors=c1.getDeclaredConstructors();//获得本类所有方法
        for (Constructor c :constructors) {
            System.out.println("全部:"+c);
        }
        //获得指定构造器  (String name, int id, int age)
        Constructor declaredConstructor = c1.getDeclaredConstructor(String.class, int.class, int.class);
        System.out.println("指定构造器:"+declaredConstructor);
        
        /*
        打印输出结果:
        public:public com.javacto.reflection.User(java.lang.String,int,int)
        public:public com.javacto.reflection.User()
        全部:public com.javacto.reflection.User(java.lang.String,int,int)
        全部:public com.javacto.reflection.User()
        指定构造器:public com.javacto.reflection.User(java.lang.String,int,int)
         */
    }
}

5.通过反射动态创建对象测试

public class Test09 {
    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {
        //获得Class对象
        Class c1 = Class.forName("com.javacto.reflection.User");

        //构造一个对象
       /* User user = (User)c1.newInstance(); //本质上调用了类的无参构造器
        System.out.println(user);  //User{name='null', id=0, age=0}*/

        //通过构造器创建对象
        Constructor constructor = c1.getDeclaredConstructor(String.class, int.class, int.class);

        User user2 = (User) constructor.newInstance("小明", 1, 18);
        System.out.println(user2); //User{name='小明', id=1, age=18}


        //通过反射调用普通方法
        User user3= (User) c1.newInstance();
        //通过反射获取一个方法
        Method setName = c1.getMethod("setName", String.class);

        //invoke: 激活的意思  (对象, "方法需要的参数")
        setName.invoke(user3,"小明3");
        System.out.println(user3.getName());


        System.out.println("========通过反射操作属性===========");
        //通过反射操作属性
        User user4= (User) c1.newInstance();
        Field name = c1.getDeclaredField("name");
        
        //不能直接操作私有属性, 我们需要关闭程序的安全检查,属性或者方法的 setAccessible(true)
        name.setAccessible(true);
        name.set(user4,"小明4"); //修改属性值
        System.out.println(user4.getName());//setAccessible 默认为false 如果没有关闭将会报没有访问private的权限
        //can not access a member of class com.javacto.reflection.User with modifiers "private"

    }
}

invoke(对象,"参数"):激活方法
setAccessible:关闭安全检查

6.获取泛型信息

Java采用泛型擦除的机制来引入泛型,Java中的泛型仅仅是给编译器javac使用的,确保数据的安全性和免去强制类型转换的问题,但是,一旦编译完成,所有和泛型有关的类型全部擦除。

思考该怎么获得? (之前有说过,类加载的时候就产生了Class对象,故class对象里面应该是有保留的)

public class Test11 {

    /**
     * 通过泛型传参
     * @param map
     * @param list
     */
    public void test01(Map<String,User> map, List <User> list){
        System.out.println("test01");

    }



    /**
     * 通过泛型返回值
     * @return
     */
    public Map<String,User> test02(){
        System.out.println("test02");
        return null;
    }


    public static void main(String[] args) throws NoSuchMethodException {
        //获得参数类型
        Method method = Test11.class.getMethod("test01", Map.class, List.class);
        //获取参数类型  即Map和 List
        Type[] genericParameterTypes = method.getGenericParameterTypes();
        for (Type genericParameterType : genericParameterTypes) {
            System.out.println("1:"+genericParameterType);

            //判断genericParameterType参数类型 是否属于 ParameterizedType 参数化类型
            if (genericParameterType instanceof ParameterizedType){
                //如果属于参数化类型,获得他的真实类型 getActualTypeArguments
                Type[] actualTypeArguments = ((ParameterizedType) genericParameterType).getActualTypeArguments();

                //再次输出真实的泛型信息
                for (Type actualTypeArgument : actualTypeArguments) {
                    System.out.println("2:"+actualTypeArgument);
                }
            }
        }
        
        
        //获得返回值类型
        method = Test11.class.getMethod("test02",null);
        Type genericReturnType = method.getGenericReturnType();
        if (genericReturnType instanceof ParameterizedType){
            //如果genericReturnType返回值类型属于参数化类型,获得他的真实类型 getActualTypeArguments
            Type[] actualTypeArguments = ((ParameterizedType) genericReturnType).getActualTypeArguments();
            
            //再次输出真实的泛型信息
            for (Type actualTypeArgument : actualTypeArguments) {
                System.out.println("3:"+actualTypeArgument);
            }
        }
    }
}

7.获取注解信息

在这里插入图片描述

public class Test12 {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
        Class c1 = Class.forName("com.javacto.reflection.Student2");

        //通过反射获取注解
        Annotation[] annotations = c1.getAnnotations();
        for (Annotation annotation : annotations) {
            System.out.println(annotation);
        }

        //获得注解的value的值   获取指定注解值
        MyTable myTable =(MyTable) c1.getAnnotation(MyTable.class);
        String value = myTable.value();
        System.out.println(value);


        //获得类指定的注解
        System.out.println("=====获得类指定的注解======");
        Field f= c1.getDeclaredField("name");
        MyField annotation = f.getAnnotation(MyField.class);
        System.out.println(annotation.columnName());
        System.out.println(annotation.type());
        System.out.println(annotation.length());
    }

}

@MyTable("db_students")
class Student2{
    @MyField(columnName = "db_id",type = "int",length = 10)
    private int id;
    @MyField(columnName = "db_age",type = "int",length = 10)
    private int age;
    @MyField(columnName = "db_name",type = "varchar",length = 50)
    private System name;

    public Student2() {
    }

    public Student2(int id, int age, System name) {
        this.id = id;
        this.age = age;
        this.name = name;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public System getName() {
        return name;
    }

    public void setName(System name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Student2{" +
                "id=" + id +
                ", age=" + age +
                ", name=" + name +
                '}';
    }
}

//类名的注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface MyTable{
    String value();
}

//属性的注解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface MyField{
    String columnName(); //列名
    String type(); //类型
    int length(); //长度
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值