IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> 狂神说Java笔记--反射和注解部分笔记 -> 正文阅读

[Java知识库]狂神说Java笔记--反射和注解部分笔记

传送门==>B站遇见狂神说–反射和注解

笔记和练习只是跟着视频整理的;但是有的知识点并没有整理进来.


1.什么是注解

Annotation: 从JDK5开始,Java增加对元数据的支持,也就是注解,注解与注释是有一定区别的,可以把注解理解为代码里的特殊标记,这些标记可以在编译,类加载,运行时被读取,并执行相应的处理。通过注解开发人员可以在不改变原有代码和逻辑的情况下在源代码中嵌入补充信息。

  • 不是程序本身,只是向程序作出解释,这一点与注释(comment)没太大区别
  • 可以被其他程序(例如:编译器)读取

注解的格式: 以 @注释名 在代码中;还可以添加参数值,例如

@SuppressWarnings(value=“unchecked”).

注解的使用位置 :可附加在package,class,method,field…;可通过反射机制编程实现对这些元数据的访问.


例如: 之前经常用到的重写方法注解 @Override

在学习拉莫达表达式时见过的 函数式接口注解 @FunctionalInterface

已弃用的方法/类/包: 弃用标识注解 @Deprecated


2.内置注解

@Override 定义在java.lang.Override 中,仅适用于注释方法;表示该方法是重写超类中的一个对应方法声明.

@Deprecated 定义在java.lang.Deprecated 中,可注释 方法,属性,类, 标示已过时(弃用);一般被该注解注释的元素中间会有一条线划过.;只是不推荐使用,而不是不能使用.

@SuppressWarnings 定义在java.lang.SuppressWarnings中,被该注解修饰的元素以及该元素的所有子元素取消显示编译器警告,例如修饰一个类,那他的字段,方法都是显示警告;

String[] value()----表示使用时需要指定参数;

案例
@SuppressWarnings(value={“deprecation”, “unchecked”}) 表示不再建议使用警告"和"未检查的转换时的警告"不再进行检查。

参数字段说明
deprecation使用了不赞成使用的类或方法时的警告
unchecked执行了未检查的转换时的警告
fallthrough当 Switch 程序块直接通往下一种情况而没有 Break 时的警告。
path在类路径、源文件路径等中有不存在的路径时的警告。
serial当在可序列化的类上缺少 serialVersionUID 定义时的警告。
finally任何 finally 子句不能正常完成时的警告。
all关于所有情况的警告。

在这里插入图片描述


3.元注解

元注解是负责为其他注解做注解;位于java.lang.annotation包下;

@Target:描述注解的使用范围(作用域)

参数: TYPE —标注"类、接口(包括注释类型)或枚举声明"。FIELD—标注"字段声明"。
METHOD —标注"方法"。PARAMETER —标注"参数"。CONSTRUCTOR —标注"构造方法"。
LOCAL_VARIABLE —标注"局部变量"。

@Retention 表示需要在什么级别保存该注释信息;用于描述注解的声明周期(SOURCE(源码级别)<CLASS(class级别)<RUNTIME运行时级别) ;一般在自定义注解时用runtime;

SOURCE:仅在源文件中,当Java文件编译为class文件的时,注解不能使用;
CLASS:注解被保留到class文件,当虚拟机加载class文件,就不能使用;
RUNTIME:虚拟机加载class文件之后,还可使用该注解

@Document 说明该注解会被包含在javadoc文档;

@Inherited 表示子类可以继承父类的这个注解


4.自定义注解

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

  • @interface用来声明一个注解,格式为:public @interface 注解名{定义内容}
  • 注解使用参数时; 参数类型+参数名( );
  • 使用注解,进行赋值时,可以不在乎顺序;因为有参数名
  • 每个方法实际上声明了一个配置参数;
  • 方法的名称就是参数名称;
  • 返回值类型就是参数类型(基本类型学;Class,String,enum);
  • 可通过default来声明参数的默认值;经常使用空字符串或0作为默认值;如果默认值为-1,表示不存在;
  • 若定义注解时;只有一个参数成员;建议参数名为value( ),那么在使用这个注解时,参数赋值还能省略value()指向;
  • 注解元素要有值,不然就写上默认值.

练习自定义注解

public class MyClass {
    //使用自定义的注解;
    //使用注解进行参数赋值时,没有顺序;
    @MyAnnotation(id = 100,age = 21,name = "小智",course = {"Java"})
    public void myMethod(){
    }

    //案例;这里使用定义了一个参数的那个注解;参数赋值还能省略value()指向;
    @MyAnnotation1("小智")
    public void myMethod1(){

    }
}

//自定义注解;
//@Target:注解的作用域;
//@Retention:注解的声明周期;
@Target(value = {ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation{
    //注解使用参数时; 参数类型,参数名();
    // 还可以用default加默认值; (一般默认值用空字符串或0);
    //如果说,这里没有声明默认值,那么在使用时注解,必须要加参数赋值;
    String name() default "";
    int age() default 0;
    //若默认值为 -1;则表示不存在;
    int id() default -1;
    //还可以是数组类型的;
    String[] course() default {"C++"};
}

//自定义注解;若只有一个参数,建议参数名使用value();
@Target(value = {ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation1{
    //那么在使用这个注解时,参数赋值还能省略value()指向;
    String value();
}

5.反射概述

Java的反射(reflection)机制是指在程序的运行状态中,可以构造任意一个类的对象,可以了解任意一个对象所属的类,可以了解任意一个类的成员变量和方法,可以调用任意一个对象的属性和方法。这种动态获取程序信息以及动态调用对象的功能称为Java语言的反射机制。反射被视为动态语言的关键。

动态语言
在运行时可以改变其结构的语言:例如新的函数,对象,甚至是代码都可被引进;已经存在的函数可以被删除或者其他的结构改变; 也就是在运行时的代码可以根据某些条件改变自身结构;
主要的动态语言:Object_C;C#,PHP,Python,JavaScript;

静态语言
运行时结构不可变的语言,例如Java,C,C++;
虽然Java不是动态语言,但 Java作为 准动态语言 ;有动态性,可使用反射机制获得类似动态语言的特性;


java reflection
反射(reflection)使得Java变为类似动态语言;反射机制允许程序在执行期借助于反射API获取任何类的内部信息,并且可以直接操作任意对象的内部属性以及方法.

Class c=Class.forName(“java.lang.String”)

一个类在内存中只有一个Class对象;一个类被加载后,整个结构都会被封装到Class对象中

加载类之后,在堆内存的方法区中产生了一个Class类型的对象(注意一个类只有一个Class对象);此对象包含了完整的类结构信息;可以通过这个对象看到类的结构.

正常方式: 引入需要的"包类"名称 —>通过new实例化—>取得实例化对象

反射方式: 实例化对象—>getClass( )方法—>得到完整的"包类"名称

反射通俗来讲就是通过对象反射求出类的名称.


6.获得反射对象

Java反射机制提供的功能

(1)在运行时判断任意一个对象所属的类; (2)在运行时构造任意一个类的对象; (3)在运行时(判断/调用)任意一个类所具有的成员变量和方法; (4) 在运行时获取泛型信息; (5)在运行时可处理注解,(6)可以生成动态代理;

Java反射的优缺点

优点:可实现动态创建对象和编译
缺点:对于性能有影响;使用反射基本上是一种解释操作;总是慢于直接执行相同的操作.

java.lang.Class:代表一个类;
java.lang.reflect.Method:代表类的方法
java.lang.reflect.Field:代表类的成员变量
java.lang.reflect.Constructor :代表类的构造器


练习:

public class MyClass {
    public static void main(String[] args) throws ClassNotFoundException {
        //通过反射获取实体类的Class对象;
        Class<?> name1 = Class.forName("com.xiaozhi.day05_javareflection.User");
        //
        System.out.println(name1);//输出为: class com.xiaozhi.day05_javareflection.User

        //一个类在内存中只有一个Class对象;一个类被加载后,整个结构都会被封装到Class对象中;
        Class<?> name2 = Class.forName("com.xiaozhi.day05_javareflection.User");
        Class<?> name3 = Class.forName("com.xiaozhi.day05_javareflection.User");
        Class<?> name4 = Class.forName("com.xiaozhi.day05_javareflection.User");
        //测试,输出他们的hashcode值;相等;
        System.out.println(name1.hashCode());
        System.out.println(name2.hashCode());
        System.out.println(name3.hashCode());
        System.out.println(name4.hashCode());
    }
}

//创建一个实体类;
class User{
    String name;
    int age;
    int id;

    public User(String name, int age, int id) {
        this.name = name;
        this.age = age;
        this.id = id;
    }

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

    public int getId() {
        return id;
    }

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

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

7.获取Class类的方式

Class类:
对象反射后可得到的信息: 类的属性,方法和构造器,这个类实现的接口;
对于每个类;jre都保留了一个不变的Class类型的对象; 一个Class对象包含了特定的结构(class/interface/enum/annotation/primitive type/void[])有关信息

Class作为一个类,它只能由系统创建对象;JVM中一个加载的类只有一个Class实例;
而一个Class对象对应的是一个加载到JVM中的一个.class字节码文件;
每个类的实例都会标记自己的Class实例来源;
通过Class可完整得到一个类中的所有被加载结构;

常用方法注释
static Class.forName()返回指定name的Class对象
getName()返回Class对象表示的实体(类,接口,数组类,void[])的名称
Object newInstance()调用缼省构造函数,返回Class对象实例
Class getSuperClass()返回当前Class对象的父类Class对象
Class[] getinterfaces()返回当前Class对象的接口
ClassLoader getClassLoader()获取类的类加载器
Constructor[]getConstructors()返回包含Constructor对象的数组
Method getMethod(String name,Class…T)返回一个Method对象,形参为paramType
Field[] getDeclaredFields()返回Field对象的一个数组

如何获取Class类的对象呢;
(1)若已知实体类;可通过类的class属性获取,安全可靠,性能高;

Class Class对象=实体类.class;

(2)已知实体类的实例,调用实例的getClass()方法获取Class对象;

Class Class对象=实体类对象.getClass();

(3)已知类的全类名,且在该类的类路径在,可使用Class类的静态方法forName()获取,但是会抛出异常
ClassNotFoundException

Class Class对象=Class.forName(“类的路径”);

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


练习:

public class Demo{
    public static void main(String[] args) throws ClassNotFoundException {
        Person person=new Student();
        System.out.println("他说=>"+person.name);//他说=>我是学生

        //获取Class类;
        //1.通过对象获取;
        Class c1 = person.getClass();
        System.out.println(c1);//class com.xiaozhi.day06_studyclass.Student
        System.out.println(c1.hashCode());//460141958

        //2.通过类路径获取;
        Class c2= Class.forName("com.xiaozhi.day06_studyclass.Student");
        System.out.println(c2);//class com.xiaozhi.day06_studyclass.Student
        System.out.println(c2.hashCode());//460141958

        //3.通过类名.class获取;
        Class c3 = Student.class;
        System.out.println(c3);//class com.xiaozhi.day06_studyclass.Student
        System.out.println(c3.hashCode());//460141958

        //可获取父类的类型;
        Class superclass = c1.getSuperclass();
        System.out.println("父类 "+superclass);//父类 class com.xiaozhi.day06_studyclass.Person


        //4.内置基本数据类型可以直接用类名.Type
        Class<Character> type = Character.TYPE;
        System.out.println(type);   //char
    }
}

//创建一个Person类;
class Person{
    String name;

    public Person(String name) {
        this.name = name;
    }

    public Person() {
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                '}';
    }
}
//创建学生类;
class Student extends Person{
    public Student() {
        this.name="我是学生";
    }
}

8.所有类型的Class对象

具有Class对象的;
class(外部类,常用内部类,静态内部类,局部内部类,匿名内部类); interface(接口); [ ](数组);enum(枚举);
annotation(注解) ;primitive type(基本数据类型) ;void.

对于数组;只要数组的类型维度一样;Class就一样;

练习:

public class Demo {
    public static void main(String[] args) {
        //类;
        Class c1=String.class;
        System.out.println(c1);//class java.lang.String
        //接口;
        Class c2=Runnable.class;
        System.out.println(c2);//interface java.lang.Runnable
        //一维数组;
        Class c3=String[].class;
        System.out.println(c3);//class [Ljava.lang.String;
        //二维数组;
        Class c4=String[][].class;
        System.out.println(c4);//class [[Ljava.lang.String;
        //注解;
        Class c5=Target.class;
        System.out.println(c5);//interface java.lang.annotation.Target
        //枚举类型;
        Class c6= ElementType.class;
        System.out.println(c6);//class java.lang.annotation.ElementType
        //void;
        Class c7=void.class;
        System.out.println(c7);//void
        //基本数据类型;
        Class c8=int.class;
        System.out.println(c8);//int

        System.out.println("===============");
        //关于数组;
        //只要数组的类型维度一样;Class就一样;
        int[]   arr0=new int[10];
        int[][] arr1=new int[1][2];
        int[][] arr2=new int[10][100];
        System.out.println(arr0.getClass().hashCode());//460141958

        System.out.println(arr1.getClass().hashCode());//1163157884
        System.out.println(arr2.getClass().hashCode());//1163157884
    }
}

9.类加载内存分析

JAVA内存简化;

堆:存放 new 的对象和数组
可被所有线程共享,不会存放别的对象引用

栈:存放基本变量类型(包含这个基本类型的具体数值);以及引用对象的变量(存放的是 引用 在 堆中的具体地址)

方法区:存放 class和static变量;
可以被所有的线程共享

类加载过程;
当程序主动使用某个类时,若该类还未被加载到内存中,则系统会通过 加载–> 链接–> 初始化.进行初始化;

加载(Load): 将类的class字节码文件读入内存,将这些静态数据转换为方法区的运行时数据结构,且创建一个java.lang.Class对象代表这个类;由类加载器完成;

链接(link): 将类的二进制数据合并到jvm的运行状态;
(1)首先确保加载的类信息符合jvm规范; (2)正式在方法区中为类变量分配内存且设置类变量默认初始值;(3)虚拟机常量池内的符号引用(常量名) 替换 为直接引用地址.

初始化(initialize):执行类构造器 <clinit>()方法的过程.
(1)类构造器 <clinit>()方法是由编译期自动收集类中所有的类变量的赋值行为和静态代码块中的语句合并产生.(类构造器是构造类信息,不是用来构造该类对象的)
(2)当初始化类时,若发现其父类还未初始化,先触发父类初始化;
(3)虚拟机确保类的<clinit>()方法在多线程环境中会加锁同步

练习

public class Demo {
    public static void main(String[] args) {
        //创建Demo2对象;
        Demo2 demo2=new Demo2();
        System.out.println(demo2.a);
        /*
        首先加载 内存;产生一个类对应Class对象;
        链接; 结束后 a=0;
        然后初始化; <clinit>(){
                      System.out.println("Demo2的静态代码块初始化");
                      a=200;
                      a=10;
               }
               所以输出的a=10;
         */
    }
}

class Demo2{
    static {
        System.out.println("Demo2的静态代码块初始化");
        a=200;
    }
    static int a=10;
    public Demo2() {
        System.out.println("Demo2的构造方法初始化");
    }
}

10.分析类的初始化

类的主动引用(会发生类的初始化)

(1)当虚拟机启动,先初始化main方法所在的类; (2)new 类的对象 ;
(3)调用类的静态成员(final常量除外)或者静态方法;
(4)使用java.lang.reflect包的方法对类进行反射调用;
(5)继承关系中,先初始化父类;

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

(1)当访问静态域时,只有真正声明此静态域的类才被初始化;
例如:通过子类引用父类的静态变量,并不会导致子类初始化;
(2)通过数组定义的类引用,不会触发此类的初始化;
(3)引用常量不会触发此类的初始化,这是由于常量在链接 时就存入调用类的常量池中


练习

主动引用;创建子类时,先加载的是父类构造初始化;

public class Demo {
    static {
        System.out.println("main方法被加载");
    }

    public static void main(String[] args) {
        //首先是主动引用;创建子类时,先加载的是父类构造初始化;
        Son son=new Son();
    }
}

//父类;
class Father{
    static int a=10;
    static {
        System.out.println("父类被加载");
    }
}
//子类;
class Son extends Father{
    static {
        System.out.println("子类被加载");
        s=100;
    }
    static int s=10;
    final static  int M=2000;
}

输出:

main方法被加载
父类被加载
子类被加载

注释掉;使用反射机制;也是主动引用.

//使用反射机制;
Class.forName("com.xiaozhi.day08_initclass.Son");

输出

main方法被加载
父类被加载
子类被加载

注释掉;试试 被动引用;子类去调用父类的静态属性;

 System.out.println(Son.a);

输出:

main方法被加载
父类被加载
10

注释掉;试试 被动引用,创建数组

//被动引用,创建数组;
Father[] f=new Father[15];

输出

main方法被加载

注释掉,试试被动引用,调用子类的常量

//被动引用; 调用常量;
System.out.println(Son.M);

输出

main方法被加载
2000

11.类加载器

类缓存:标准的JavaSe类加载器可按要求查找类,一旦某个类被加载到类加载器中,将维持加载(缓存一段时间),但JVM可以回收这些Class对象.

源程序(.java文件) —>Java编译器—>字节码(.class)文件 —>类装载器 —>字节码校验器—>解释器—>操作系统平台

类加载器 把类装载进内存

引导类加载器 使用C++编写的,作为JVM自带的类加载器,负责Java平台核心库,用来装载核心类库;注意该加载器无法直接获取.

扩展类加载器 负责jre/lib/ext目录下的jar包或者 java.ext.dirs指定目录下的jar包装入工作库.

系统类加载器 负责java -classpath 或者 java.class.path 指定的目录下的类与jar包装入库

练习

public class Demo {
    public static void main(String[] args) throws ClassNotFoundException {

        //获取系统加载器;
        ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
        System.out.println(systemClassLoader);//sun.misc.Launcher$AppClassLoader@18b4aac2

        //获取系统加载器的父类加载器;---->扩展类加载器;
        ClassLoader systemClassLoaderParent = systemClassLoader.getParent();
        System.out.println(systemClassLoaderParent);//sun.misc.Launcher$ExtClassLoader@1b6d3586

        //获取扩展加载器的父类加载器--->根加载器; (使用C/C++写的;)
        ClassLoader parent = systemClassLoaderParent.getParent();
        System.out.println(parent);//null

        //查看当前类 的 加载器;  ---->系统加载器;
        ClassLoader loader = Class.forName("com.xiaozhi.day09_aboutclassloader.Demo").getClassLoader();
        System.out.println(loader);//sun.misc.Launcher$AppClassLoader@18b4aac2

        //查看系统内置类的加载器; --->根加载器
        ClassLoader loader1 = Class.forName("java.lang.Object").getClassLoader();
        System.out.println(loader1); //null

        //获取系统类加载器可加载的路径;
        System.out.println(System.getProperty("java.class.path"));
    }
}

12.获取类的运行时结构

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

getName() 获取类的具体类名
getSimpleName() 获得类名;

getField(“指定属性”) 仅获取指定public属性
getFields( ) 仅获取public属性

getDeclaredField(“指定属性”) 获取指定属性
getDeclaredFields( ):可获取到所有属性

getMethods( ) :可获取类的以及父类的所有public方法
getDeclaredMethods() :仅获取本类的所有方法

getMethod("方法名" ,参数类型.class):获取指定的方法;注意(若为无参方法则在参数类型处使用null)

getConstructors();获取public 构造方法
getDeclaredConstructors():获取所有构造方法
getDeclaredConstructor(类型.class,类型.class)获取指定的构造方法;


练习

public class Demo {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException {
        //获取类的Class对象;
        Class<?> c = Class.forName("com.xiaozhi.day10_classruntime.MyClass");

        //1.getName() 获取类的具体类名
        String name = c.getName();
        System.out.println(name);//com.xiaozhi.day10_classruntime.MyClass
        //2.getSimpleName()获得类名;
        String simpleName = c.getSimpleName();
        System.out.println(simpleName);//MyClass

        System.out.println("----------------------------");
        //3.获得类的属性;
        //getFields() 只能找到public属性;
        Field[] fields = c.getFields();

        //getDeclaredFields():可获取到所有属性;
        Field[] fields1 = c.getDeclaredFields();
        for (Field field : fields1) {
            System.out.println(field);
            /*
        private java.lang.String com.xiaozhi.day10_classruntime.MyClass.name
         private int com.xiaozhi.day10_classruntime.MyClass.age
         */
        }
        //getDeclaredField: 获取指定属性;
        Field age = c.getDeclaredField("age");
        System.out.println(age);//private int com.xiaozhi.day10_classruntime.MyClass.age

        System.out.println("----------------------------");

        //4.获取类的方法;

        //getMethods :可获取类的以及父类的所有public 方法;
        Method[] methods = c.getMethods();
        for (Method method : methods) {
            System.out.println(method);
        }
        /*
            public java.lang.String com.xiaozhi.day10_classruntime.MyClass.toString()
            public java.lang.String com.xiaozhi.day10_classruntime.MyClass.getName()
            public void com.xiaozhi.day10_classruntime.MyClass.setName(java.lang.String)
            public void com.xiaozhi.day10_classruntime.MyClass.setAge(int)
            public int com.xiaozhi.day10_classruntime.MyClass.getAge()
            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()
         */
        System.out.println("----------------------------");

        //getDeclaredMethods():仅获取本类方法;
        Method[] declaredMethods = c.getDeclaredMethods();
        for (Method declaredMethod : declaredMethods) {
            System.out.println(declaredMethod);
        }
        /*
            public java.lang.String com.xiaozhi.day10_classruntime.MyClass.toString()
            public java.lang.String com.xiaozhi.day10_classruntime.MyClass.getName()
            public void com.xiaozhi.day10_classruntime.MyClass.setName(java.lang.String)
            public void com.xiaozhi.day10_classruntime.MyClass.setAge(int)
            public int com.xiaozhi.day10_classruntime.MyClass.getAge()
         */
        System.out.println("----------------------------");
        //5.获得指定方法;需要给参数值;
        Method getAge = c.getMethod("getAge", null);
        System.out.println(getAge);//public int com.xiaozhi.day10_classruntime.MyClass.getAge()
        Method setAge = c.getMethod("setAge", int.class);
        System.out.println(setAge);//public void com.xiaozhi.day10_classruntime.MyClass.setAge(int)

        System.out.println("----------------------------");

        //6.获取类的构造方法;

        //getConstructors();获取public 构造方法
        Constructor<?>[] constructors = c.getConstructors();
        for (Constructor<?> constructor : constructors) {
            System.out.println(constructor);
        }
        /*
        public com.xiaozhi.day10_classruntime.MyClass()
        public com.xiaozhi.day10_classruntime.MyClass(java.lang.String,int)
         */

        //getDeclaredConstructors():获取所有构造方法
        Constructor<?>[] declaredConstructors = c.getDeclaredConstructors();
        for (Constructor<?> declaredConstructor : declaredConstructors) {
            System.out.println(declaredConstructor);
        }
        /*
        public com.xiaozhi.day10_classruntime.MyClass()
        public com.xiaozhi.day10_classruntime.MyClass(java.lang.String,int)
         */

        //getDeclaredConstructor(@Nullable Class<?>... parameterTypes)获取指定的构造方法;
        Constructor<?> myConstructor = c.getDeclaredConstructor( String.class, int.class);
        System.out.println(myConstructor);
        //public com.xiaozhi.day10_classruntime.MyClass(java.lang.String,int)

    }
}
//自定义的实体类;
class MyClass{
    private String name;
    private int age;

    public MyClass() {
    }
    public MyClass(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 "MyClass{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

13.动态创建对象执行方法

使用反射获取类的Class对象;调用newInstance() ;创建对象
实际上是调用了类的无参构造器;

通过构造器创建对象;
先得到Class对象;在通过Class对象调用获取构造器;
然后 通过得到的构造器调用newInstance()方法;创建对象

使用反射,可以调用类的方法;
用Class对象调用getMethod( 方法名,类型),获取Method对象;
使用Object invoke(对象,设置的值) 方法激活;即可调用.


对于Object invoke(对象,设置的值) 方法;
Object作为原方法的返回值;若原方法无返回值,则返回null;
若原方法为静态方法,则参数:对象可以为null;
若原方法是无参数的,则设置的值为null;
若原方法为private私有修饰的,在调用invoke()之前,应该先调用方法对象的setAccessible(true)方法.

元素为private私有的;不能直接操作私有属性,要先关闭程序的安全监测;
使用方法setAccessible(true) 设置为可通过检查;该方法默认为false

setAccessible方法时用来启动/禁用访问安全检查


练习;

public class Demo {
    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {
        //获取类的Class对象;
        Class c = Class.forName("com.xiaozhi.day11_creatobjformethod.MyClass");

        //构造对象;
        //newInstance() ; 实际上是调用了类的无参构造器;
        MyClass myClass = (MyClass)c.newInstance();
        //System.out.println(myClass);
        //MyClass{name='null', age=0}

        //通过构造器创建对象;
        Constructor declaredConstructor = c.getDeclaredConstructor(String.class, int.class);
        MyClass myClass1 = (MyClass) declaredConstructor.newInstance("小智", 21);
        //System.out.println(myClass1);
        //MyClass{name='小智', age=21}

        MyClass myClass2 = new MyClass();
        //通过反射调用方法;
        //先获取方法;
        Method setAge = c.getMethod("setAge", int.class);
        //invoke(对象,值):激活方法
        setAge.invoke(myClass2, 88);
        //System.out.println(myClass3.getAge());
        //88

        //通过反射操作类的属性;
        MyClass myClass3 = new MyClass();
        Field age = c.getDeclaredField("age");

        //由于在实体类中age属性为private私有的;不能直接操作私有属性,可关闭程序的安全监测
        //setAccessible(布尔值) 设置是否可访问;默认为false
        age.setAccessible(true);
        age.set(myClass3,1000);
        System.out.println(myClass3.getAge());
        //1000
    }
}
//自定义的实体类;
class MyClass{
    private String name;
    private int age;

    public MyClass() {
    }
    public MyClass(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 "MyClass{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

14.性能对比分析

练习

//这里用的MyClass类为自定义类;

public class Demo {

    //普通方法调用;
    public static void test() {
        MyClass myClass=new MyClass();

        long start=System.currentTimeMillis();
        for (int i = 0; i < 1000000000; i++) {
            myClass.getAge();
        }
        long end=System.currentTimeMillis();
        System.out.println("普通方法调用=>"+(end-start)+"ms");
    }

    //反射方式调用;
    public static void  test2() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        MyClass myClass = new MyClass();
        //获取Class对象;
        Class c = myClass.getClass();
        //获取指定方法;
        Method method = c.getDeclaredMethod("getAge",null);

        long start=System.currentTimeMillis();
        for (int i = 0; i < 1000000000; i++) {
            method.invoke(myClass,null);
        }
        long end=System.currentTimeMillis();
        System.out.println("反射方式调用=>"+(end-start)+"ms");
    }

    //反射方式调用, 且关闭监测;
    public static void  test3() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        MyClass myClass = new MyClass();
        //获取Class对象;
        Class c = myClass.getClass();
        //获取指定方法;
        Method method = c.getDeclaredMethod("getAge",null);
        //关闭监测
        method.setAccessible(true);

        long start=System.currentTimeMillis();
        for (int i = 0; i < 1000000000; i++) {
            method.invoke(myClass,null);
        }
        long end=System.currentTimeMillis();
        System.out.println("反射方式且关闭监测调用=>"+(end-start)+"ms");
    }

    public static void main(String[] args) {
        test();
        try {
            test2();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        try {
            test3();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
       /* 普通方法调用=>3ms
        反射方式调用=>2225ms
        反射方式且关闭监测调用=>1764ms*/
    }
}

15.获取泛型信息

java采用了泛型擦除机制来引入泛型;泛型仅为编译器javac使用;确保数据的安全性以及避免强制类型转换问题;编译完成后就会擦除.

为通过反射操作这些类型;java新增ParameterizedType,GenericArrayType,TypeVariable,WildcardType 表示不能被归一到Class类中的类型但有和原始类型同名的类型;

ParameterizedType,表示参数化类型;例Collection< String>
GenericArrayType,表示元素类型为参数化类型或类型变量的数组类型;
TypeVariable, 各种类型变量的公共父接口;
WildcardType ; 通配符类型表达式


练习

public class Demo {
    public void test1(Map<MyClass,String> map, List<MyClass> list){
        System.out.println("方法一");
    }
    public Map<MyClass,String> test2(){
        System.out.println("方法二");
        return null;
    }

    public static void main(String[] args) throws NoSuchMethodException {
        //获取类的指定方法一;
        Method method1 = Demo.class.getMethod("test1", Map.class, List.class);
        //获得方法一的泛型参数类型;
        Type[] types = method1.getGenericParameterTypes();
        for (Type type : types) {
            System.out.println("方法1=>"+type);
            //方法1=>java.util.Map<com.xiaozhi.day13.MyClass, java.lang.String>
            //方法1=>java.util.List<com.xiaozhi.day13.MyClass>

            //进行参数判断;
            if(type instanceof ParameterizedType){
                //获得真实类型;
                Type[] actualTypeArguments = ((ParameterizedType) type).getActualTypeArguments();
                for (Type actualTypeArgument : actualTypeArguments) {
                    System.out.println("方法1真实类型=>"+actualTypeArgument);
                    //方法1真实类型=>class com.xiaozhi.day13.MyClass
                    //方法1真实类型=>class java.lang.String

                    //方法1真实类型=>class com.xiaozhi.day13.MyClass
                }
            }
        }
        System.out.println("----------------------------------------------");

        //获取类的指定方法二;
        Method method2 = Demo.class.getMethod("test2",null);
        //获得方法二的 返回值 泛型;
        Type types2 = method2.getGenericReturnType();
            //进行参数判断;
            if(types2 instanceof ParameterizedType){
                //获得真实类型;
                Type[] actualTypeArguments = ((ParameterizedType) types2).getActualTypeArguments();
                for (Type actualTypeArgument : actualTypeArguments) {
                    System.out.println("方法2真实类型=>"+actualTypeArgument);
                    //方法2真实类型=>class com.xiaozhi.day13.MyClass
                    //方法2真实类型=>class java.lang.String
                }
            }
    }
}

16.获取注解信息

首先得到类的Class对象;
调用getAnnotation(指定注解) 方法;获取指定的注解对象;
然后使用注解对象.参数值;调用获取注解的参数值;

练习

//通过反射获取注解信息;
public class Demo {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
        //获取类的Class对象;
        Class c = Class.forName("com.xiaozhi.day14.MyClass");
        //获取注解;
        Annotation[] annotations = c.getAnnotations();
        for (Annotation annotation : annotations) {
            System.out.println(annotation);
            //@com.xiaozhi.day14.ClassAnnotation(value=小智)
        }

        //获取注解;
        ClassAnnotation annotation = (ClassAnnotation)c.getAnnotation(ClassAnnotation.class);
        //获取注解的value值;
        System.out.println(annotation.value());
        //小智;

        //获得类的指定注解;
        //先获得类的指定属性;
        Field name = c.getDeclaredField("name");
        name.setAccessible(true);
        //在通过属性获取属性的注解;
        FieldAnnotation annotation1 = name.getAnnotation(FieldAnnotation.class);
        //获取注解对应参数的值;
        System.out.println(annotation1.columnName());
        System.out.println(annotation1.type());
        System.out.println(annotation1.length());
        //name
        //String
        //10
    }
}
//自定义类;模拟数据表;
@ClassAnnotation("小智")
class MyClass{
    @FieldAnnotation(columnName = "id",type = "int",length = 10)
    private int id;
    @FieldAnnotation(columnName = "age",type = "int",length = 3)
    private int age;
    @FieldAnnotation(columnName = "name",type = "String",length = 10)
    private String name;

    public MyClass() {
    }

    public MyClass(int id, int age, String 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 String getName() {
        return name;
    }

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

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


//自定义类注解
//作用域;类、接口(包括注释类型)或枚举声明;
//生命周期为运行时;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface ClassAnnotation{
    String value();
}

//自定义属性注解;
//作用域:属性;
//生命周期为运行时;
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface FieldAnnotation{
    //字段名;
    String columnName();
    //类型;
    String type();
    //字段长度
    int length();
}

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2021-08-22 13:25:07  更:2021-08-22 13:27:17 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2025年1日历 -2025/1/31 2:11:02-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码