跟狂神学注解和反射笔记

笔记是根据狂神的B站视频,以及自己的理解做出来的。如果有说错的地方,望大佬能够指出,相互学习!

注解和反射

  • 注解
    • 元注解
    • 测试元注解
    • 自定义元注解
  • 反射机制
    • 动态语言和静态语言
    • Java Reflection
    • java 放射机制及应用
    • java反射优点和缺点
    • 反射相关的主要API
      • Class类
    • 类加载内存分析
    • 类的加载与ClassLoader的理解
      • 什么时候会发生初始化
      • 什么时候不会发生初始化
      • 类加载器
    • 创建运行时类的对象
      • 获取运行时类的完整结构
    • 有了Class对象,能做什么
      • 调用指定的方法 invoke
      • setAccessible
    • 获取泛型信息
    • 获取注解信息
      • 反射操作注解

注解

  • 和注释一样,注解不是程序本身,而是对程序作出解释,而注解与注释不同的点在于,注解可以被其他程序比如编译器读取

//内置注解@Override//重写注解
@Deprecated//不推荐使用注解,可以使用但是又风险或者有更好的方式
@SuppressWarnings//“镇压”警告注解

元注解

元注解 重点需要了解 @Target @Retention

  • 元注解的作用就是注解其他注解,Java定义了4个标准的meta-annotation类型,他们被用来提供对其他annotation类型做说明
  • 4个元注解分别为:
    • @Target:用于描述注解的使用范围
    • @Retention:用于表示需要在什么级别保存注解信息,用于描述注解的声明周期,(SOURCE
    • @Document:说明该注解将被包含在javadoc(文档)中
    • @Inherited:说明子类可以继承父类中的该注解

测试元注解

//测试元注解
@MyAnnotation
public class Test02 {public void test(){}}//定义注解
//Target  表示我们的注解可以用在哪些地方  METHOD方法  TYPE 类
@Target(value = {ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented //表示把注解生成在Javadoc中
@Inherited //表示可以被继承
@interface MyAnnotation{}

自定义元注解

使用@interface自定义注解时,自动继承了java.lang.annotation.Annotation接口

image-20201214001642498

public class Test03 {//注解可以显示赋值,如果没有默认值,就必须给注解赋值@MyAnnotation2(schools = {"贵州大学","清华大学"})public void test(){}//如果注解只有一个参数可以省略参数名,或value@MyAnnotation3("")public void test2(){}
}
//注解的写法
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation2{//注解的参数:类型+参数名() [default 默认值];String name() default "";int age() default 0;int id() default -1;  //如果默认值为-1,代表不存在String[] schools();
}@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation3{//如果注解只有一个值  可以直接使用valueString[] value();  //不成名的一个规范
}

反射机制


动态语言和静态语言

动态语言

  • 在运行时可以改变其结构:例如新的函数、对象甚至代码可以被引进,已有的函数可以被删除或者是其他结构上的变化。通俗来说就是运行时代码可以根据一些条件来改变自身的结构。
  • 主要动态语言:Object-C、C#、JavaScript、PHP、Python等。

这里演示下 javaScript :弱语言(是比较随意的)

function f(){var x="var a=3;var b=5;alert(a+b)";eval(x);  //执行
}

静态语言

  • 与动态语言相对应的,运行时不能改变其结构,如Java、C、C++
  • Java不是动态语言,但是java可以称为是“准动态语言”。即java有一定的动态性,可以利用反射机制获得类似动态语言的特性。Java的动态性使得编程时更加灵活。

Java Reflection

  • Reflection(反射)是Java被视为动态语言的关键,反射机制允许程序在执行期间借助于Reflection API获取任何类的内部信息(比如类名,类的接口,类的方法,字段,属性…),并且能够直接操作任意对象的内部属性及方法。

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

    image-20201214004432882

java 放射机制及应用

image-20201214011853001

java反射优点和缺点

优点:

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

缺点:

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

反射相关的主要API

image-20201214012551359

代码演示:

这里提到的hash code:是一种编码方式,在Java中,每个对象都会有一个hashCode,Java可以通过这个hashCode来识别一个对象。

public class Test02 {public static void main(String[] args) throws ClassNotFoundException {//通过反射获取类的class对象Class c1 = Class.forName("com.javacto.reflection.User");//需要全限定名System.out.println(c1); //class com.javacto.reflection.UserClass c2 = Class.forName("com.javacto.reflection.User");Class c3 = Class.forName("com.javacto.reflection.User");//打印hashcode可以看出一个类在内存中只有一个Class对象,不可能有多个//一个类被被载后,类的整个结构都会被封装在Class对象中System.out.println(c2.hashCode());System.out.println(c3.hashCode());}}//实体类  :pojo  entity
class User{private String name;private int id;private int age;/*** 无参构造方法*/public User() {}/*** 有参构造方法* @param name* @param id* @param age*/public User(String name, int id, int age) {this.name = name;this.id = id;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 int getAge() {return age;}public void setAge(int age) {this.age = age;}@Overridepublic String toString() {return "User{" +"name='" + name + '\'' +", id=" + id +", age=" + age +'}';}
}

运行结果:

image-20201214015933525

Class类

image-20201214012701857

从这张图可以看出:可以通过对象反射出类的名称 这就是反射

image-20201214221042011

  • Class类的常用方法
方法名说明
static Class forName(String name)返回指定类名name对应的Class对象
Object newInstance()调用缺省构造函数,返回Class对象的一个实例
String getName()返回此Class对象所表示的实体(类、接口、数组类或者void)的名称
Class getSuperClass返回当前Class对象的父类Class对象
Class[] getinterfaces()获取当前Class对象的接口
ClassLoader getClassLoader()返回该类的加载器
Constructor[] getConstructors()返回一个包含某些Constructor对象的数组
Method getMothed(String name,Class… T)返回一个Method对象,此对象形参类型为param Type
Fied[] getDeclaredFields()返回Field对象的一个数组

  • 获得Class类的实例

    a)如果已有具体的类,通过类的class属性获取,最为安全可靠且性能最高的方法。
    C l a s s 变 量 = X X X . c l a s s ; Class 变量=XXX.class; Class=XXX.class;

    b)已知某个类的实例,调用此实例的getClass()方法获取Class对象。
    C l a s s 变 量 = x x x . g e t C l a s s ( ) ; Class 变量=xxx.getClass(); Class=xxx.getClass();

    c)已知一个类的全名且在类路径下,可以通过Class类的静态方法forName()获取,需要处理异常ClassNotFoundException
    C l a s s 变 量 = C l a s s . f o r N a m e ( " 已 知 类 的 全 限 定 名 " ) ; Class 变量=Class.forName("已知类的全限定名"); Class=Class.forName("");

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

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

测试class 类的创建方式有哪些

public class Test03 {public static void main(String[] args) throws ClassNotFoundException {Person person=new Student();System.out.println("这个人是:"+person.name);//方式1:通过对象获得Class c1=person.getClass();System.out.println(c1.hashCode());  //460141958//方式2:forName获得Class c2=Class.forName("com.javacto.reflection.Student");System.out.println(c2.hashCode());  //460141958//方式3: 通过类名.class 获得Class c3 = Student.class;System.out.println(c3.hashCode());  //460141958
//---------------------------------------------------------------------------------------//方式4: 基本内置类型的包装类都有一个 TYPE属性  类名.TYPEClass c4 = Integer.TYPE;System.out.println(c4);  //int  默认的基本类型 (d.获得内置基本数据类型)//获得父类类型Class c5 = c1.getSuperclass();System.out.println(c5);  //class com.javacto.reflection.Person}
}//定义一个父类
class Person{public String name;public Person() {}public Person(String name) {this.name = name;}@Overridepublic String toString() {return "Person{" +"name='" + name + '\'' +'}';}
}//子类1: 学生
class Student extends Person{public Student(){this.name="学生";}
}//子类2:老师
class Teacher extends Person{public Teacher(){this.name="老师";}
}

运行结果:可以看到 前三种通过不同的方式获得Class对象一样(这个class对象是Student,因为对象实例化的是Student这个类)

image-20201214230745746


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

所有类的Class对象

public class Test04 {public static void main(String[] args) {Class c1= Object.class;//类Class c2= Comparable.class;//接口Class c3= String[].class;//一维数组Class c4= int[][].class;//二维数组Class c5=Override.class;//注解Class c6= ElementType.class;//枚举Class c7= Integer.class;//基本数据类型Class c8= void.class;//void (代表空类型)Class c9= Class.class;//ClassSystem.out.println(c1);  //class java.lang.ObjectSystem.out.println(c2);  //interface java.lang.ComparableSystem.out.println(c3);  //class [Ljava.lang.String;System.out.println(c4);  //class [[ISystem.out.println(c5);  //interface java.lang.OverrideSystem.out.println(c6);  //class java.lang.annotation.ElementTypeSystem.out.println(c7);  //class java.lang.IntegerSystem.out.println(c8);  //voidSystem.out.println(c9);  //class java.lang.ClassSystem.out.println("=======================");//只要元素类型与维度一样,就是同一个classint[] a=new int[10];int[] b=new int[100];int[][] c=new int[10][10];System.out.println(a.getClass().hashCode());System.out.println(b.getClass().hashCode());System.out.println(c.getClass().hashCode());}
}

查看运行结果:

image-20201214234925277

  • 注意:只要元素类型与维度一样,就是同一个class,反之 同样是int类型的数组,维度不同Class对象所打印出的hashcode不同,即:数组维度不同对应不同的Class对象。

类加载内存分析

java内存分析

image-20201215001210878

了解:类的加载过程

image-20201215001307449

类的加载与ClassLoader的理解

image-20201215001451972

测试demo以及画图分析

public class Test05 {public static void main(String[] args) {A a =new A();System.out.println(A.m);/*** 1.加载到内存,会产生一个对应Class对象* 2.链接, 链接结束后 m=0* 3.初始化(){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;
}

image-20201215023915425

基于图的分析步骤:

1.类写好后,编译成class加载到内存中(方法区可以说是特殊的堆,可以看到里面存储class相关的信息包含方法的信息)

2.在类加载的时候形成了class对象

3.准备执行main方法,这里的m是静态变量,链接段初始值为为0,这些内存都将在方法区中进行分配(栈内存空间:存放方法中的局部变量)

4.之后开始执行代码 堆内存(我的理解是只要new出来的就存在堆内存),

new A(); 产生新的对象 A类对象 (A类自己创建出来的对象)先去找到自己Class类,通过A类的具体方法进行赋值。

5.初始化,这一步是由jvm去执行的,赋值后通过 方法 初始化 详情看代码 最终结果就为m=100.

查看代码的运行结果:

image-20201215030911361

额外补充:

静态代码块:用staitc声明,jvm加载类时执行,仅执行一次
构造代码块:类中直接用{}定义,每一次创建对象时执行。
执行顺序优先级:静态块,main(),构造块,构造方法。

有关 静态代码块,构造代码块,构造函数执行执行顺序查看该链接(写得很详细)

查看

什么时候会发生初始化

类的主动应用(一定会发生类的初始化)

image-20201216212714239

测试代码: 类什么时候会初始化

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;  //常量
}

运行效果:

image-20201216212954000

可以看出:当虚拟机启动时,用户需要指定一个要执行的主类(包含main()方法的那个类),虚拟机会先初始化这个主类。且初始化一个类的时候,如果发现其父类还没有进行过初始化,则需要先触发其父类的初始化。

什么时候不会发生初始化

类的被动引用,不会发生初始化

image-20201216224014964

基于同样的代码测试如下:

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;  //常量
}

1.对于静态字段,只有直接定义这个字段的类才会被初始化,因此通过其子类来引用父类中定义的静态字段,**只会触发父类的初始化而不会触发子类的初始化。**在本例中,虚拟机会先发现其父类Father还未被初始化,因此虚拟机将先初始化父类Father,而Son始终不会被初始化。

image-20201216224516034

2.通过数组定义来引用类,不会触发此类的初始化

image-20201216230814644

上述案例运行之后并没有任何输出,说明虚拟机并没有初始化类Son。但是,这段代码触发了另外一个名为 [Lcom.javacto.reflection.Son的类的初始化。从类名称我们可以看出,这个类代表了元素类型为Son的一维数组,它是由虚拟机自动生成的,直接继承于Object的子类,创建动作由字节码指令newarray触发。

3.常量在编译阶段会存入调用类的常量池中,本质上并没有直接引用到定义常量的类,因此不会触发定义常量的类的初始化

常量不会引起父类与子类的初始化,因为所有的常量以及静态变量,都是在链接阶段都已经赋了一个值了,初始化的时候已经存在了

image-20201216230211304

类加载器

类加载器的作用

注:类是有缓存的 是提高效率的 在加载的时间当中 如果有不需要的会垃圾回收器 (gc)回收这些class对象 。

image-20201217000342891

image-20201217000505709

java平台核心库:rt.jar 包

扩展类加载器 :ExtClassLoader:

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

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

image-20201217011931885

测试获取加载器:

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);  //根加载器加载的}
}

运行结果:

image-20201217014622799

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

扩充: 获得系统类加载器可以加加载的路径

如何获得系统类加载器可以加加载的路径
System.out.println(System.getProperty("java.class.path"));
//打印输出的结果 ,可以看到 java运行环境路径,扩展包类jar包 rt.jar包  包括我们自己写的工程,ide的jar包。当然如果我们的类不在这些地方就读取不到了,加载是有机制的
/*D:\java\tools\jdk1.8.0_201\jre\lib\charsets.jar;D:\java\tools\jdk1.8.0_201\jre\lib\deploy.jar;D:\java\tools\jdk1.8.0_201\jre\lib\ext\access-bridge-64.jar;D:\java\tools\jdk1.8.0_201\jre\lib\ext\cldrdata.jar;D:\java\tools\jdk1.8.0_201\jre\lib\ext\dnsns.jar;D:\java\tools\jdk1.8.0_201\jre\lib\ext\jaccess.jar;D:\java\tools\jdk1.8.0_201\jre\lib\ext\jfxrt.jar;D:\java\tools\jdk1.8.0_201\jre\lib\ext\localedata.jar;D:\java\tools\jdk1.8.0_201\jre\lib\ext\nashorn.jar;D:\java\tools\jdk1.8.0_201\jre\lib\ext\sunec.jar;D:\java\tools\jdk1.8.0_201\jre\lib\ext\sunjce_provider.jar;D:\java\tools\jdk1.8.0_201\jre\lib\ext\sunmscapi.jar;D:\java\tools\jdk1.8.0_201\jre\lib\ext\sunpkcs11.jar;D:\java\tools\jdk1.8.0_201\jre\lib\ext\zipfs.jar;D:\java\tools\jdk1.8.0_201\jre\lib\javaws.jar;D:\java\tools\jdk1.8.0_201\jre\lib\jce.jar;D:\java\tools\jdk1.8.0_201\jre\lib\jfr.jar;D:\java\tools\jdk1.8.0_201\jre\lib\jfxswt.jar;D:\java\tools\jdk1.8.0_201\jre\lib\jsse.jar;D:\java\tools\jdk1.8.0_201\jre\lib\management-agent.jarD:\java\tools\jdk1.8.0_201\jre\lib\plugin.jar;D:\java\tools\jdk1.8.0_201\jre\lib\resources.jar;D:\java\tools\jdk1.8.0_201\jre\lib\rt.jar;D:\java\project\ideaPro\Test\ProjectAll\out\production\注解和放射;D:\java\常用工具\ideaIU-2019.3.2.win\lib\idea_rt.jar*/

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

当某个类加载器需要加载某个.class文件时,它首先把这个任务委托给他的上级类加载器,递归这个操作,如果上级的类加载器没有加载,自己才会去加载这个类。

双亲委派机制的作用

1、防止重复加载同一个.class。通过委托去向上面问一问,加载过了,就不用再加载一遍。保证数据安全。
2、保证核心.class不能被篡改。通过委托方式,不会去篡改核心.clas,即使篡改也不会去加载,即使加载也不会是同一个.class对象了。不同的加载器加载同一个.class也不是同一个Class对象。这样保证了Class执行安全。

创建运行时类的对象

获取运行时类的完整结构

通过反射获取运行时类的完整结构

  • Field(字段)、Method(方法)、Constructor(构造器)、Superclass(父类)、Interface(接口)、Annotation(注解)
    • 实现的全被接口
    • 所继承的父类
    • 全部的构造器
    • 全部的方法
    • 全部的Field
    • 注解
    • 。。。

操作很简单,总结只需要注意:

默认 只能找到 public 字段, 属性,方法 。。。。。

关键字:Declared 能找到全部的 字段, 属性,方法 。。。。。

//获得类的信息
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.UserUser*/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.nameprivate int com.javacto.reflection.User.idprivate 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)*/}
}

有了Class对象,能做什么

  • 创建类的对象,调用Class对象的newInstance()方法

    1. 必须要有一个无参数的构造器

    2. 类的构造器的访问权限要够

  • 通过获取Class对象的构造器创建

    1. 通过Class对象的getDeclaredConstructor(所需要的参数 Type.class) 方法获取本类指定参数类型的构造器

    2. 向构造器传入一个对象数组进去,里面包含此构造器所需要的各个参数

    3. 通过Constructor实例化对象

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

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"}
}

执行结果:

image-20201218010601616

调用指定的方法 invoke

invoke 激活的意思

(对象,“方法所需要的参数”)

image-20201218010718267

setAccessible

启动和禁用安全检查的开关,默认为false (启动)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QJM02udB-1608384690640)(C:\Users\Cyj\AppData\Roaming\Typora\typora-user-images\image-20201218010740137.png)]

分析性能问题

public class Test10 {//普通方式调用public static void test01(){User user=new User();//开始时间long starTime=System.currentTimeMillis();//模拟10亿次不断获取名字所需要的时间for (int i = 0; i < 1000000000; i++) {user.getName();}//结束时间long endTime=System.currentTimeMillis();System.out.println("普通方式执行10亿次:"+(endTime-starTime)+"ms");}//反射方式调用public static void test02() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {User user = new User();Class c1 = user.getClass();//获取指定方法Method getName = c1.getDeclaredMethod("getName", null);//开始时间long starTime = System.currentTimeMillis();//模拟10亿次不断获取名字所需要的时间for (int i = 0; i < 1000000000; i++) {getName.invoke(user, null);}//结束时间long endTime = System.currentTimeMillis();System.out.println("反射方式执行10亿次:"+(endTime-starTime)+"ms");}//反射方式调用  关闭检测public static void test03() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {User user = new User();Class c1 = user.getClass();//获取指定方法Method getName = c1.getDeclaredMethod("getName", null);getName.setAccessible(true);//开始时间long starTime = System.currentTimeMillis();//模拟10亿次不断获取名字所需要的时间for (int i = 0; i < 1000000000; i++) {getName.invoke(user, null);}//结束时间long endTime = System.currentTimeMillis();System.out.println("关闭检测执行10亿次:"+(endTime-starTime)+"ms");}public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {test01();test02();test03();//如果反射调用次数多的话,可以关闭这个检测 提高程序的一个效率}
}

执行结果:

普通方式执行最快,

反射会比正常的效率慢,

关闭检测执行改善了反射调用效率问题

image-20201218011841308

  • 注意:其中setAccessible(true)方法调用后会关闭对应属性、方法的安全检查,但会改善反射调用的效率问题

如果反射调用频率高的话,可以关闭这个检查 以便提高程序的一个效率

获取泛型信息

  • 反射操作泛型

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

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

  • 为了通过反射操作这些类型,Java新增了ParameterizedType,GenericArrayType,TypeVariable和WildcardType几种类型来代表不能被归一到Class类中的类型但是又和原始类型齐名的类型。

类型说明
ParameterizedType表示一种参数化类型,比如Collection< String >
GenericArrayType表示一种元素类型是参数化类型或者类型变量的数组类型
TypeVariable是各种类型变量的公共父接口
WildcardType代表一种通配符类型表达式

通过反射获取泛型 扩展了解

public class Test11 {/*** 通过泛型传参* @param map* @param list*/public void test01(Map map, List  list){System.out.println("test01");}/*** 通过泛型返回值* @return*/public Map 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和 ListType[] genericParameterTypes = method.getGenericParameterTypes();for (Type genericParameterType : genericParameterTypes) {System.out.println("1:"+genericParameterType);//判断genericParameterType参数类型 是否属于 ParameterizedType 参数化类型if (genericParameterType instanceof ParameterizedType){//如果属于参数化类型,获得他的真实类型 getActualTypeArgumentsType[] 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返回值类型属于参数化类型,获得他的真实类型 getActualTypeArgumentsType[] actualTypeArguments = ((ParameterizedType) genericReturnType).getActualTypeArguments();//再次输出真实的泛型信息for (Type actualTypeArgument : actualTypeArguments) {System.out.println("3:"+actualTypeArgument);}}}
}

查看结果: 得到泛型

image-20201218023547327


获取注解信息

反射操作注解

image-20201218031249308

练习反射操作注解

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;}@Overridepublic 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(); //长度
}

执行结果:

image-20201218031614874


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部