注解与反射
1.注解
1.1什么是注解
Java.Annotation是从JDK5.0开始引入的新技术
-
注解和反射是所有框架的底层实现机制
-
注释是给人看的,注解不仅给人看,还能给程序看
Annotation的作用:
- 不是程序本身,可以对程序做出解释(这一点和注释comment没什么区别)
- 可以被其他程序(比如:编译器等)读取
Annotation的格式:
- @注解名
- 还可以添加参数值,例如
@SuppressWarnings(value=“unchecked”)
Annotation在哪里使用:
- 可以附加在package、class、method、field等上面,相当于给他们添加了额外的辅助信息
- 我们可以通过反射机制编程 实现对这些元数据的访问
1.2内置注解
Java.Annotation有3个内置注解
-
@Override:定义在java.lang.Override中,只能修饰method,表示子类打算覆写父类的方法的声明
-
@Deprecated:定义在java.lang.Deprecated中,可以修饰class、method、field,表示不鼓励程序员使用这样的元素,通常是因为它很危险或者存在更好的替代实现
-
@SuppressWarnings:定义在java.lang.SuppressWarnings中,用来抑制编译时的警告信息
其需要参数,但是这些参数都是定义好的:
@SuppressWarnings("all") @SuppressWarnings("unchecked") @SuppressWarnings(value={"unchecked","deprecation"}) 等等...
测试:
public class Test01 extends Object{
@Override
public String toString(){
return super.toString();
}
@Deprecated
public static void test02(){
System.out.println("Deprecated");
}
@SuppressWarnings("all")
public void test01(){
System.out.println("SuppressWarnings");
}
public static void main(String[] args) {
test02();
}
}
1.3元注解
-
Java.Annotation定义了4个标准的元注解meta-annotation类型,他们被用来解释对其他注解
@Target: 注解可以被用在哪些地方,如class、method、field; @Retention:注解的生命周期,在什么级别保存该注解信息 source<class<runtime; @Document:说明该注解将被包含在javadoc中; @Inherited:说明子类可以继承父类的注解;
-
4个元注解+@interface 可以自定义注解
public class Test02 { @Myannotation public void test01(){ System.out.println("自定义注解"); } } //定义一个注解,一般只要@Target和@Retention @Target(value={ElementType.TYPE,ElementType.METHOD,ElementType.FIELD}) @Retention(value= RetentionPolicy.RUNTIME) @Documented @Inherited @interface Myannotation{ }
1.4自定义注解
使用@interface自定义注解时,自动继承了java.lang.annotation.Annotation接口
分析:
- @interface用来声明一个注解,格式为:
public @interface 注解名{…}
在类里面声明需要把public去掉 - 其中的配置参数,写成了方法的形式,但是仍是参数。
- 参数类型只能是基本类型,Class,String,enum
- 可以通过default来声明参数的默认值,如空字符串、0
- 如果只有一个参数成员,一般参数名为value
- 注解参数如果没有默认值,就必须有值
测试:
public class Test02 {
//去掉哪一个注解的默认值,就只用给那一个注解的默认值赋值就可以了,注解没有顺序
//注解可以显示赋值,如果没有默认值,我们就必须给注解赋值
@Myannotation1
public void test01(){
System.out.println("自定义注解1");
}
@Myannotation2("joseph")
public void test02(){
System.out.println("自定义注解2");
}
}
//定义一个注解,一般只要@Target和@Retention
@Target(value={ElementType.TYPE,ElementType.METHOD,ElementType.FIELD})
@Retention(value= RetentionPolicy.RUNTIME)
@Documented
@Inherited
@interface Myannotation1{
//注解的参数:参数类型 + 参数名();
//加了default之后,@MyAnnotation()就可以不在里面加参数 也可以赋值
String name() default "";
int age() default 0;
int id() default -1; //如果默认值为-1,代表不存在;与indexof类似,如果找不到就返回-1;
String[] scholls() default {"qinghua","peking"};
}
//定义一个注解,一般只要@Target和@Retention
@Target(value={ElementType.TYPE,ElementType.METHOD,ElementType.FIELD})
@Retention(value= RetentionPolicy.RUNTIME)
@Documented
@Inherited
@interface Myannotation2{
//如果只有一个参数,则名字使用value;在使用注解时,名字可省略
String value();
}
2.反射
Java.Reflection
静态语言和动态语言:
-
动态语言:
-
是一类在运行时可以改变其结构的语言:例如新的函数、对象、甚至代码可以被引进,已有的函数可以被删除或是其他结构上的变化。通俗讲就是在运行时代码可以根据某些条件改变自身结构。
-
常见动态语言:JavaScript、PHP、Python、C#、Object-C
//体现动态语言的代码 function test() { var x = "var a=3;var b=5;alert(a+b)"; eval(x);//在运行时改变函数代码 }
-
-
静态语言:
- 与动态语言相对应的,运行时结构不可变的语言就是静态语言。如Java、C、C++。
- Java本身不是动态语言,但是Java可以称之为“准动态语言”。即Java有一定的动态性,我们可以利用反射机制获得类似动态语言的特性。Java的动态性让编程的时候更加灵活。
2.1反射引入
-
Reflection(反射)是Java被视为动态语言的关键
-
反射机制 允许程序在执行期间 借助Reflection API 取得任何类的内部信息,并能直接操作任意对象的的内部属性及方法。
Class c = Class.forName("java.lang.String")
1. Class类及Class类的对象:
-
Java中存在一个Class类型的类,即Java中的类分为Class类和其他类;
-
当某个类加载完之后,在堆内存的方法区中就产生了这个类的Class类型的对象
区分“类”和这个“类的Class对象”,通过类的Class对象可以得到这个类的全部信息。
-
每个类在内存中都只有一个Class对象,我们可以通过“类的对象”(以及类名等)得到这个类对应的Class对象。通过这个对象我们可以看到类的结构。
这个Class对象就像是一面镜子,通过这面镜子可以看到类的结构,所以我们形象的称之为反射。
**反射定义:**每个类都唯一对应一个Class对象,通过这个对象我们可以得到这个类的所有信息。获取某个类Class对象的过程——我们称之为反射。
2.正常方式和反射方式new对象:
//正常方式
Student stu1=new Student();
//反射方式
Student stu2=Class.forName("java.lang.String").newInstance();
Student stu3=stu1.getClass().newInstance();
3.举例:一个类只存在一个Class对象
实体类User:
public class User {
private int id;
private String name;
private int age;
//无参构造
public User() {
}
//有参构造
public User(int id, String name, int age) {
this.id = id;
this.name = name;
this.age = age;
}
//getter和setter
public int getId() {
return id;
}
public void setId(int id) {
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;
}
//toString()
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
'}';
}
}
测试:
public class Test01 {
public static void main(String[] args) throws ClassNotFoundException {
//通过反射获取类对应的Class对象
//一个类被加载后,类的整个结构信息会被放到对应的Class对象中
Class<?> c1 = Class.forName("com.kuang.reflection.User");
System.out.println(c1);
//每个类都有一个Class对象
Class<?> c2 = Class.forName("com.kuang.reflection.User");
System.out.println(c1.hashCode());
System.out.println(c2.hashCode()); //两次获取的User的Class对象hashCode一致,表明是同一个Class对象
}
}
4.反射机制提供的功能
在运行时:
-
判断任意一个对象所属的类
-
判断任意一个类所具有的的成员变量和方法
-
调用任意一个对象对应类的成员变量和方法
-
构造任意一个类的对象
-
获取泛型信息
-
处理注解
-
生成动态代理……
5.反射的优点和缺点
优点:
- 可以实现动态创建对象和编译,体现出很大的灵活性。
缺点:
- 对性能有影响
- 使用反射基本上是一种解释操作,我们可以告诉JVM,我们希望做什么并且它能满足我们的要求
- 这类操作总是慢于 直接执行相同的操作
6.反射相关的主要API
- java.lang.Class:代表一个类
- java.lang.reflect.Method:代表类的方法
- java.lang.reflect.Field:代表类的成员变量
- java.lang.reflect.Constructor:代表类的构造器
……
2.2Class类及Class对象
1.区分Class类和其他类,每个类都有唯一对应的Class对象
-
Object类被所有类继承,其中定义了
public final Class getClass()
方法,因此所有的类的对象都能调用该方法获取 自己对应类 对应的Class对象。 -
该方法返回类型是一个Class类,此类是Java反射的源头
-
实际上所谓反射 从程序的运行结果来看 也很好理解,即:可以通过类的对象 反射得到 类对应Class对象,从而得到类的所有信息
- 对象照镜子(反射)以后可以得到的信息:某个类的属性、方法、构造器;实现了哪些接口;
- 没有每个类而言,JRE都为其保留一个不变的Class对象,一个Class对象包含了特定某个结构(class/interface/enum/annotation/primitive type/void/[])的有关信息。
2.Class对象理解:
-
Class本身也是一个类,只不过是比较特殊的类。
-
Class对象只能由系统建立
-
一个加载的类在JVM中只会有一个Class实例对象
-
一个Class对象对应的是一个加载到JVM中的 .class文件
-
通过Class对象可以完整地得到一个类中所有被加载的结构
-
每个类的实例都会记得自己是由哪个Class实例所生成
-
Class类是Reflection的根源,针对任何你想动态加载、运行的类,唯有先获得相应的Class对象
3.获取Class对象的方式
每个类都继承了Object类,Object类中有一个静态成员变量class
,和getClass()方法
-
通过对象获得
Class<? extends Person> c1 = p1.getClass();
-
通过Class.forName(“包名+类名”)获得
Class<?> c2 = Class.forName("com.kuang.reflection.Student");
-
通过类的静态成员class获得
Class<?> c3 =Person.class;
-
针对内置的基本数据类型
Class<?> c4 =Integer.TYPE;
-
还可以利用ClassLoader,后面讲解
测试:
public class Person {
private String name;
public Person(){
}
public Person(String name){
this.name=name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
'}';
}
}
public class Student extends Person{
public Student(){
super.setName("学生");
}
}
public class Teacher extends Person{
public Teacher(){
super.setName("老师");
}
}
//**************************************
public class Test02 {
public static void main(String[] args) throws ClassNotFoundException {
Person p1=new Student();
System.out.println("这个人是:"+p1.getName());
//获取Class对象方法一:通过对象获得,如果是子类的实现,赋值给父类,得到的时子类对应的Class对象
Class<? extends Person> c1 = p1.getClass();
//获取Class对象方法二:通过Class.forName("类名+包名")获得
Class<?> c2 = Class.forName("com.kuang.reflection.Student");
//获取Class对象方法三:通过类的静态成员变量class获得
Class<?> c3 =Person.class;
//获取Class对象方法四:只针对内置的基本数据类型
Class<?> c4 =Integer.TYPE;
//获取父类的Class
Class<?> superc1 = c1.getSuperclass();
System.out.println(c1);
System.out.println(c2);
System.out.println(c3);
System.out.println(c4);
System.out.println(superc1);
}
}
4.Class类的常用方法
-
Object newInstance()
调用类的缺省构造函数,返回类的一个对象实例 -
getName()
返回Class对象所表示的实体 的名称(类的名称) -
Class getSuperClass()
得到父类的Class对象 -
Class[] getinterfaces()
得到当前Class对象对应的类 的所有接口 对应的Class对象 -
ClassLoader getClassLoader()
返回该类的类加载器 -
Constructor[] getConstructors()
返回一个包含某些Constructor对象的数组 -
method getMothed(String name, Class.. T)
返回一个Method对象,此对象的形参类型为paramType -
Filed[] getDeclaredFields()
返回Field对象的一个数组
5.哪些类型可以有Class对象
在Java中,万物皆类,万物皆对象
- class:外部类,成员(成员内部类,静态内部类),局部内部类,匿名内部类
- interface:接口
- []:数组
- enum:枚举
- annotation:注解
- primitive type:基本数据类型
- void
测试:
public class Test03 {
public static void main(String[] args) {
//测试哪些类型有Class对象
Class c1=Object.class; //类
Class c2=Comparable.class; //接口
Class c3=int[].class; //一维数组
Class c4=int[][].class; //二维数组
Class c5= ElementType.class; //枚举
Class c6=Override.class; //注解
Class c7=Integer.class; //基本数据类型
Class c8=void.class; //void
Class c9=Class.class;
System.out.println(c1);
System.out.println(c2);
System.out.println(c3);
System.out.println(c4);
System.out.println(c5);
System.out.println(c6);
System.out.println(c7);
System.out.println(c8);
System.out.println(c9);
//只有元素类型和维度一样,就对应一个Class对象
int[] a=new int[10];
int[] b=new int[100];
Class Ca=a.getClass();
Class Cb=b.getClass();
System.out.println(Ca==Cb);
}
}
2.3Java内存分析
1.Java内存
-
栈:
- 一般用来存放用关键字new出来的数据
-
堆:
-
基本数据类型
-
局部变量(在方法代码段中定义的变量),方法调用完后JVM回收
-
引用数据类型——即需要用关键字new出来的对象所对应的引用 也是存放在栈空间中。
此时JVM在栈空间中给对象引用分配了一个地址空间,存储引用变量,
指向,在堆空间给该引用指向的实际对象分配的地址空间。
-
-
方法区:
- 用于存放已被加载的类信息、常量、static静态变量、即时编译器编译后的代码等数据
2.4类的加载
1.类的加载过程
-
类加载是一个将.class字节码文件读入内存,并实例化为Class对象且进行相关初始化的过程
-
java的类使用时才被加载,且类的加载过程只发生一次
java类的加载过程可分为三个步骤:加载、链接、初始化
区分“类的加载过程”和其步骤“加载”
“类的初始化”只是“类的加载过程”的一个步骤
-
加载:
-
将.class字节码文件读入内存,将静态数据转换成方法区的运行时数据结构
-
生成这个类对应的Class对象
Class对象是加载到内存中才会产生的,由JVM创建,我们只能获取,不能生成
通过反射动态进行的
-
-
链接:将Java类的二进制代码 合并到 JVM的运行状态之中
- 验证:确保类信息符合JVM规范
- 准备:为static变量分配内存并设置初始值(在类初始化之前做),在内存的方法区进行
- 解析:JVM常量池内的符号引用(常量名)替换为直接引用(地址)
-
初始化:。
-
JVM执行类构造器
<clinit>()方法
的过程。类构造器
<clinit>()方法
是由编译期 自动收集类中所有类变量的赋值动作和static代码块中的语句合并产生的。类构造器是构造类信息的,不是构造该类对象的构造器。
-
当初始化一个类的时候,如果发现其父类还没有进行初始化,则需要先触发其父类的初始化。
-
JVM会保证一个类的
<clinit>()方法
在多线程环境中被正确加锁和同步。
-
代码测试:
public class Test05 {
public static void main(String[] args) {
A a=new A();
System.out.println(A.m);
}
}
class A{
static {
System.out.println("A类静态代码快初始化");
m=300;
}
static int m=100;
public A(){
System.out.println("A类的无参构造初始化");
}
}
过程:
-
加载阶段:加载Test05类和A类,在方法区分别生成其对应的Class对象
-
链接阶段:为static变量m,在方法区分配内存空间,并进行默认初始化
static int m=0;
-
初始化阶段:类的
<clinit>()方法
将static变量赋值和static代码合并,再执行赋值System.out.println("A类静态代码快初始化"); m=300; m=100; //定义阶段已经在链接阶段完成
2.类加载时机和初始化时机
-
**类加载时机:**从执行包含main函数的类开始加载,涉及到下面情况的类就加载
- 使用类的静态变量或静态方法
- 使用反射方式来强制创建某个类或接口 对应的java.lang.Class对象
- 创建类的实例
- 初始化某个类的子类
- (直接使用java。exe命令来运行某个主类)
-
**类的初始化理解:**类的初始化 主要就是对类的static变量进行初始化
区分实例变量和静态变量,实例变量在new对象时初始化
-
JVM执行类构造器
<clinit>()方法
的过程。类构造器
<clinit>()方法
是由编译期 自动收集类中所有类变量的赋值动作和static代码块中的语句合并产生的。类构造器是构造类信息的,不是构造该类对象的构造器。
-
当初始化一个类的时候,如果发现其父类还没有进行初始化,则需要先触发其父类的初始化。
-
JVM会保证一个类的
<clinit>()方法
在多线程环境中被正确加锁和同步。
-
-
会发生类初始化的情况(类的主动引用):
- 当JVM启动,先初始化main方法所在的类
- 使用new关键字创建一个类的对象
- 调用该类的static变量(final的常量除外)和static方法
- 使用java.lang.reflect包对该类进行反射调用
- 当初始化一个类时,如果其父类没有被初始化,则会先初始化其父类
-
不会发生类初始化的情况(类的被动引用):
-
引用static常量不会触发此类的初始化(常量在链接阶段就存入调用类的常量池中了)
-
当访问一个静态成员变量时,只有真正声明这个静态成员的类才会被初始化,当通过子类引用父类的静态成员变量时,不会导致子类的初始化
-
通过数组定义类引用,不会触发此类的初始化。
数组只是一个名字和一片空间
-
测试:
//测试类什么时候会初始化
public class Test06 {
static {
System.out.println("Main类被加载");
}
public static void main(String[] args) throws ClassNotFoundException {
//1.主动引用,会发生类的初始化
Son son = new Son();
/*
Main类被加载
父类被加载
子类被加载
*/
//反射也会产生主动引用
Class.forName("com.kuang.reflection.Son");
/*
Main类被加载
父类被加载
子类被加载
*/
//2.被动引用,不会触发类的初始化
//通过子类调用父类static变量或者方法
System.out.println(Son.b);
/*
Main类被加载
父类被加载
2
*/
//通过数组定义类引用
Son[] array=new Son[5];
/*
Main类被加载
*/
//类的常量
System.out.println(Son.M);
/*
Main类被加载
1
*/
}
}
class Father{
static final int M=1;
static int b=2;
static{
System.out.println("父类被加载");
}
}
class Son extends Father{
static{
System.out.println("子类被加载");
m=300;
}
static int m=100;
}
3.类加载器
什么是类加载器?
类加载器 就是 加载字节码文件(.class)的类
Java程序是一种具有动态性的解释性语言,类(class)只有被加载到JVM中后才能运行。
当运行指定程序时,JVM会将编译生成的.class文件按照需求和一定的规则加载到内存中,组织成为一个完整的Java应用程序。
这个加载的过程是由类加载器来完成的。具体来说,就是由ClassLoader和它的子类来实现的。
类加载器本身也是一个类,其实质是把类文件从硬盘读取到内存中
类加载的方式:
- 隐式加载:使用new等方式创建对象,会隐式调用类加载器把对应的类加载到JVM中
- 显式加载:通过反射直接调用Class.forName()方法把所需的类加载到JVM中。
类加载器分类:
在Java语言中,类的加载是动态的,它并不会一次性将所有类全部加载后再运行。而是保证程序运行的基础类(例如基类)完全加载到JVM中。
其他类,在需要时才加载。
- 引导类加载器:BootStapLoader,加载最基础的文件
- 用C++编写,是JVM自带的类加载器,负责Java平台核心类库。
- 该加载器程序无法直接获取。
- 在
jre/lib/rt.jar
包下
- 扩展类加载器:ExtClassLoader,加载基础的文件
- 在
jre/lib/ext/*.jar
包下
- 在
- 应用类加载器:AppClassLoader,加载三方jar包和自己编写的java文件
- 在
CLASSPATH指定的所有jar和目录
- 在
类加载器的协调工作:双亲委派机制
3种类加载器是通过委托方式实现:
当加载一个类,JVM会自底向上去找是否已经加载;
且不能存在同名,如我们不能定义java.lang.Spring类;
双亲委派机制会检测安全性,保证创建出的类的唯一性
- 为了防止同名包、类与jdk中相冲突
- 实际加载类的时候,先通知appClassLoader看appClassLoader是否已经缓存;没有的话,appClassLoader又委派给他的父类加载器(extClassLoader)询问,看他是不是已经缓存;没有的话,exClassLoader又委派给他的父类BootStapLoader询问,看他是不是已经缓存或者能加载,有就加载;没有再返回extClassLoader,exClassLoader能加载就加载,不能的话再返回给自己的appClassLoader加载。在返回的路上,谁能加载,加载的同时也放到缓存当中去。
- 正是由于刚开始时自底向上不停地找自己的父级,所以才有Parents加载机制,翻译过来叫双亲委派机制。
加载时:
-
自底向上检查类是否已经装载
-
自顶向下尝试加载类
获取测试:
public class Test07 {
public static void main(String[] args) {
//获取AppClassLoader
ClassLoader appClassLoader=ClassLoader.getSystemClassLoader();
System.out.println(appClassLoader);
//获取ExtClassLoader
ClassLoader extClassLoader=appClassLoader.getParent();
System.out.println(extClassLoader);
//获取BootStapClassLoader
ClassLoader bootStapClassLoader=extClassLoader.getParent();
System.out.println(bootStapClassLoader);
}
//测试当前变量是由那个加载器加载的
ClassLoader curClassLoader=Class.forName("com.kuang.reflection.Test07").getClassLoader();
}
可以看出:
Test07类是由AppClassLoader加载的。
因为BootStapLoader先搜索指定目录找不到,其次ExtClassLoader也找不到,最后AppClassLoader在ClassPath中找到了Test07类。
【注意】BootStapLoader是用C++语言实现的,所以在Java语言中看不到它,输出null
类加载器 加载过程 分为以下3步:
- 装载(将.class文件放入内存,生成Class对象)
- 链接(1.检查:检查待加载.class文件的正确性;2.准备:给类中静态变量分配存储空间并默认初始化;3.解释:将符号引用转换成直接引用)
- 初始化(对静态变量和静态代码块执行初始化工作)
2.5利用反射获取Class对象 获取运行时类的完整结构
-
Field、Method、Constructor、Superclass、Interface、Annotation
-
在实际操作中,取得类的信息的操作代码,并不会经常开发
-
一定要熟悉java.lang.reflect包的作用,反射机制
-
如何取得一个类的属性、方法、构造器名称,修饰符等
测试:
public class Test08 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException {
Class<?> c1 = Class.forName("com.kuang.reflection.User");
User user=new User();
c1=user.getClass();
//1.获得类的名字
System.out.println(c1.getName()); //获得包名+类名
System.out.println(c1.getSimpleName()); //获得类名
//2.获得类的属性
System.out.println("========================");
Field[] fields = c1.getFields(); //只能找到public属性
fields=c1.getDeclaredFields(); //找到当前类的所有属性
for (Field field : fields) {
System.out.println(field);
}
/*
private int com.kuang.reflection.User.id
private java.lang.String com.kuang.reflection.User.name
private int com.kuang.reflection.User.age
*/
//找到指定属性
Field name = c1.getDeclaredField("name");
System.out.println(name);
//3.获得类的方法
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("Declare:"+method);
}
Method getName = c1.getMethod("getName", null); //获取指定方法
Method setName = c1.getMethod("setName", String.class);
System.out.println("指定的:"+getName);
System.out.println("指定的:"+setName);
//4.获得类的构造方法
System.out.println("========================");
Constructor<?>[] constructors = c1.getConstructors();
for (Constructor<?> constructor : constructors) {
System.out.println("正常的:"+constructor);
}
constructors = c1.getDeclaredConstructors();
for (Constructor<?> constructor : constructors) {
System.out.println("Declare:"+constructor);
}
Constructor<?> declaredConstructor = c1.getDeclaredConstructor(int.class, String.class, int.class);
System.out.println("指定的:"+declaredConstructor);
}
}
结果:
com.kuang.reflection.User
User
========================
private int com.kuang.reflection.User.id
private java.lang.String com.kuang.reflection.User.name
private int com.kuang.reflection.User.age
private java.lang.String com.kuang.reflection.User.name
========================
正常的:public java.lang.String com.kuang.reflection.User.toString()
正常的:public java.lang.String com.kuang.reflection.User.getName()
正常的:public int com.kuang.reflection.User.getId()
正常的:public void com.kuang.reflection.User.setName(java.lang.String)
正常的:public void com.kuang.reflection.User.setId(int)
正常的:public int com.kuang.reflection.User.getAge()
正常的:public void com.kuang.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()
Declare:public java.lang.String com.kuang.reflection.User.toString()
Declare:public java.lang.String com.kuang.reflection.User.getName()
Declare:public int com.kuang.reflection.User.getId()
Declare:public void com.kuang.reflection.User.setName(java.lang.String)
Declare:public void com.kuang.reflection.User.setId(int)
Declare:public int com.kuang.reflection.User.getAge()
Declare:public void com.kuang.reflection.User.setAge(int)
指定的:public java.lang.String com.kuang.reflection.User.getName()
指定的:public void com.kuang.reflection.User.setName(java.lang.String)
========================
正常的:public com.kuang.reflection.User()
正常的:public com.kuang.reflection.User(int,java.lang.String,int)
Declare:public com.kuang.reflection.User()
Declare:public com.kuang.reflection.User(int,java.lang.String,int)
指定的:public com.kuang.reflection.User(int,java.lang.String,int)
2.6反射有了Class对象以后 能做什么?
1.创建对象
-
直接利用newInstance()创建一个对象,适用于存在无参构造
User user = (User) c1.newInstance();
-
反射获取构造器,进而创建对象,适用于有参构造
Constructor<?> constructor = c1.getDeclaredConstructor(int.class, String.class, int.class); User user2 = (User) constructor.newInstance(1, "kuangshen", 18);
2.反射调用类中方法
User user3=(User)c1.newInstance();
//通过反射获取一个方法
Method setName = c1.getDeclaredMethod("setName", String.class);
//invoke使用方法(对象,方法的参数)
setName.invoke(user3,"kuangshen2");
System.out.println("user3:"+user3);
3.反射操作属性
//3.利用反射操作属性
User user4=(User)c1.newInstance();
Field name = c1.getDeclaredField("name");
//不能直接操作私有属性,需要开启允许访问,关闭安全检测。方法、字段、构造器都可以设置开启允许访问
name.setAccessible(true);
//操作属性(对象,属性值)
name.set(user4,"kuangshen3");
System.out.println("user4:"+user4);
测试:
public class Test09 {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {
//获得Class对象
Class<?> c1 = Class.forName("com.kuang.reflection.User");
//1.1直接利用newInstance()创建一个对象,仅适用于存在无参构造
User user = (User) c1.newInstance();
System.out.println("user:"+user);
//1.2反射获取构造器,进而创建对象,使用于有参构造
Constructor<?> constructor = c1.getDeclaredConstructor(int.class, String.class, int.class);
User user2 = (User) constructor.newInstance(1, "kuangshen", 18);
System.out.println("user2:"+user2);
//2.通过反射调用方法
User user3=(User)c1.newInstance();
//通过反射获取一个方法
Method setName = c1.getDeclaredMethod("setName", String.class);
//invoke使用方法(对象,方法的值)
setName.invoke(user3,"kuangshen2");
System.out.println("user3:"+user3);
//3.利用反射操作属性
User user4=(User)c1.newInstance();
Field name = c1.getDeclaredField("name");
//不能直接操作私有属性,需要开启允许访问,关闭安全检测。方法、字段、构造器都可以设置开启允许访问
name.setAccessible(true);
name.set(user4,"kuangshen3");
System.out.println("user4:"+user4);
}
}
setAccessible:
- Method和Field、Constructor对象都有setAccessible方法。
- 开启允许利用反射访问私有字段、方法
- 提高反射的效率。如果代码中必须用反射,而该句代码需要频繁的被调用,请设置为true
普通方法调用 和反射调用方法 性能对比:
public class Test10 {
//普通方法调用
public static void test01(){
User user=new User();
long startTime=System.currentTimeMillis();
for (int i = 0; i < 1000000000; i++) {
user.getName();
}
long endTime=System.currentTimeMillis();
System.out.println("普通方式执行10亿次:"+(endTime-startTime)+"ms");
}
//反射调用方法
public static void test02() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
User user=new User();
Class<? extends User> c1 = user.getClass();
Method getName = c1.getDeclaredMethod("getName", null);
long startTime=System.currentTimeMillis();
for (int i = 0; i < 1000000000; i++) {
getName.invoke(user,null);
}
long endTime=System.currentTimeMillis();
System.out.println("反射方式执行10亿次:"+(endTime-startTime)+"ms");
}
//反射调用方法
public static void test03() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
User user=new User();
Class<? extends User> c1 = user.getClass();
Method getName = c1.getDeclaredMethod("getName", null);
getName.setAccessible(true);
long startTime=System.currentTimeMillis();
for (int i = 0; i < 1000000000; i++) {
getName.invoke(user,null);
}
long endTime=System.currentTimeMillis();
System.out.println("关闭权限检测方式执行10亿次:"+(endTime-startTime)+"ms");
}
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
test01();
test02();
test03();
}
}
4.反射操作获取泛型
-
Java采用泛型擦除机制 来引入泛型
Java中的泛型仅仅是给编译器javac使用的,确保数据的安全性和免去强制类型转换问题,Java新增了ParameterizedType、GenericArrayType、TypeVariable和WildcardType几种类型来代表不能被归一到Class类中但是又能和原始类型齐名的类型。
-
ParameterizedType 表示参数化类型,比如Collection
-
GenericArrayType 表示一种元素类型是参数化类型或者类型变量的的数组类型
-
TypeVariable 是各种类型变量的公共父接口
-
WildcardType 代表一种通配符类型表达式
测试:
public class Test11 {
public void test01(Map<String,User> map, List<User> list){
System.out.println("test01");
}
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);
//获得参数类型及其内部泛型
Type[] genericParameterTypes = method.getGenericParameterTypes();
for (Type genericParameterType : genericParameterTypes) {
System.out.println("参数类型:"+genericParameterType);
if(genericParameterType instanceof ParameterizedType){
Type[] actualTypeArguments = ((ParameterizedType) genericParameterType).getActualTypeArguments();
for (Type actualTypeArgument : actualTypeArguments) {
System.out.println("参数类型内部泛型:"+actualTypeArgument);
}
}
}
//获得返回类型及其内部泛型
method = Test11.class.getMethod("test02", null);
Type genericReturnType = method.getGenericReturnType();
System.out.println("返回类型:"+genericReturnType);
if(genericReturnType instanceof ParameterizedType){
Type[] actualTypeArguments = ((ParameterizedType) genericReturnType).getActualTypeArguments();
for (Type actualTypeArgument : actualTypeArguments) {
System.out.println("返回类型内部泛型:"+actualTypeArgument);
}
}
}
}
2.7反射读取注解
**ORM:**object relationship Mapping 对象关系映射
类对应表
属性对应字段
对象对应记录
要求:利用注解和反射完成类和表结构的映射关系
-
框架会在类里面定义大量的注解,通过反射去读取,生成相应的信息
-
假设数据库有一张表,可以通过注解去定义和表对应的类型,通过注解生成一些数据库的语言,然后进行增删改查,自动创建表
反射读取注解实例:
package com.kuang.reflection;
import java.lang.annotation.*;
import java.lang.reflect.Field;
//测试ORM:对象关系映射
//使用反射读取注解信息三步:
//1.定义注解
//2.在实体类中实用注解
//3.使用反射获取注解,一般都是现成框架实现,我们手动实现
public class Test12 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
//反射,Class对象可以获取类的全部信息
Class<?> c1 = Class.forName("com.kuang.reflection.Student");
//1.获得这个类的注解
Annotation[] annotations = c1.getAnnotations();
for (Annotation annotation : annotations) {
System.out.println(annotation);
}
//2.获得类的指定注解,指出属性名称,只有一个属性value
TableKuang tableKuang = c1.getAnnotation(TableKuang.class);
System.out.println(tableKuang.value());
//2.获取类指定注解的值,指出属性名称,三个属性
Field name = c1.getDeclaredField("name");
FieldKuang fieldKuang = name.getAnnotation(FieldKuang.class);
System.out.println(fieldKuang.columnName()+"-->"+fieldKuang.type()+"-->"+fieldKuang.length());
//我们可以根据得到的类的信息,通过JDBC生成相关的SQL语句,执行就可以动态生成数据库表
}
}
@TableKuang("db_student")
class Student{
@FieldKuang(columnName = "db_id",type = "int",length = 10)
private int id;
@FieldKuang(columnName = "db_name",type = "varchar",length = 10)
private String name;
@FieldKuang(columnName = "db_age",type = "int",length = 3)
private int age;
public Student() {
}
public Student(int id, String name, int age) {
this.id = id;
this.name = name;
this.age = age;
}
public int getId() {
return id;
}
public void setId(int id) {
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;
}
@Override
public String toString() {
return "Student{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
'}';
}
}
//表名注解,只有一个参数,建议使用value命名
@Target(value={ElementType.TYPE})
@Retention(value= RetentionPolicy.RUNTIME)
@interface TableKuang{
String value();
}
//属性注解
@Target(value={ElementType.FIELD})
@Retention(value=RetentionPolicy.RUNTIME)
@interface FieldKuang{
String columnName();//列名
String type();
int length();
}