注解与反射
注解
作用:可以被其他程序读取,不是程序本身,可以对程序做出解释。
使用方式:@注释名,还可以添加一些参数
Annotation使用位置:附加在package,class ,method,field等上面,相当于增加额外的辅助信息。可以通过反射机制编程实现对这些元数据的访问。
常用内置注解
@Override:只适用于修饰方法,表明明一个方法打算重写超类中的另一个方法的声明。
@Deprecated:用于修饰方法,属性,类,不推荐程序员使用,或者有更好的方式。
@SuppressWarnings:用于抑制编译时的警告信息。
- suppreessWarnings(“all”):
import java.util.ArrayList;
import java.util.List;
public class Test01 extends Object {
//override是一种注解,重写的注解,用于表明方法的重写
@Override
public String toString() {
return super.toString();
}
@Deprecated //不推荐程序员使用,或者更好的方法
public static void test(){
System.out.println("Deprecated");
}
@SuppressWarnings("all")//镇压警告
public static void test2(){
List list=new ArrayList();
}
//注解
public static void main(String[] args) {
test();
test2();
}
}
元注解
负责注解其他注解(4个标准的META-annotation)
@Target:用于描述注解的使用范围
@Retention:表示需要在什么级别保存注释该注释信息,用于描述注解的生命周期。
SOURCE(源代码)<CLASS<RUNTIME(运行时,通常注解为运行时)
@Document:说明注解将被包含在javadoc中
@Inherited:说明子类可以继承父类中的该注解。
例:定义一个简单的注解
import java.lang.annotation.*;
import static java.lang.annotation.ElementType.*;
public class Test2 {
@myannotation//定义自己的注解,在方法处@注解才可以成功
public void test(){
}
}
//定义一个注解,Target表示注解可以用到哪些地方
@Retention(RetentionPolicy.RUNTIME)//只在运行时有效
@Target(value ={ElementType.METHOD,ElementType.TYPE})
@Documented//表示是否将我们的注解生成在JAVADOC中
@interface myannotation{
}
自定义注解
(public)@interface 注解名 {定义内容}
自定义注解,如果在类中声明,需要去掉public
1.其中的每一个方法其实是声明了一个配置参数
2.方法的名称就是参数的名称
3.返回值类型就是参数的类型
4.可以通过default来声明参数的默认值
5.如果只有一个参数成员,一般参数名为value
6.注解元素必须有之,我们定义注解元素时,经常使用空字符串,0为默认值。
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
public class Test3 {
/*
自定义一个注解
*/
//注解可以显示赋值,如果没有默认值,就必须给注解赋值
@MyAnnotation(name="hello")
public void test(){}
@MyAnnotation1("hello+")
public void test2(){}
}
//定义一个注解可以在类上定义,也可以在方法上定义
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)//注解生命周期
@interface MyAnnotation{
//注解参数:参数类型+参数名()
String name() default "";//默认值设置可以为空
int age() default 0;
int id() default -1;//默认值为-1代表不存在
String[] schools() default {"hello","world"};
}
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)//注解生命周期
@interface MyAnnotation1{
String value();
}
反射机制
使java实现动态语言的功能,反射机制允许程序在执行期借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法
Class c=Class.forName(“java.lang.string”)
加载完类之后,在堆内存的方法区总就产生了一个Class类型 的对象,这个对象就包含了完整类的结构信息,可以通过这个对象看到类的结构。
- 正常方式:引入需要的包名名称->通过new实例化 ->取得实例化对象
- 反射方式:实例化对象->getClass()方法->得到完整的包类名称
获得反射对象
反射相关的主要API:
java.lang.Class:代表一个类
java.lang.reflect.method:代表类的方法
java.lang.reflect.Field:代表类的成员变量
java.lang.reflect.Constructor:代表类的构造器
在Object类中定义:public final Class getClass(),返回类型是Class,可以通过对象反射求出类的名称。
Class本身也是一个类,Class对象只能由系统创建对象。
Class类常用方法
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xtQ2tUWx-1615889393504)(C:\Users\lenovo\AppData\Roaming\Typora\typora-user-images\image-20210316094607792.png)]
获取Class类的实例
1.已知具体类,通过类的class属性获取,该方法最安全可靠
Class cla=Persson.class
2.已知某个类的实例,调用该实例的getClass()方法获取Class对象
Class cla=Person.getClass();
3.已知一个类的全类名,并且该类在类路径下,可以使用Class类的静态方法forName()获取
Class cla=Class.forName(包名.类名)
//测试Class获取方法有哪些
public class Test05 {
public static void main(String[] args) {
person person=new student();
System.out.println("这个人是"+person.name);
//1.通过对象获得
Class c1=person.getClass();
System.out.println(c1.hashCode());
//2.通过forName获得
try {
Class c2=Class.forName("student");
System.out.println(c2);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
//3.通过:类名.class
Class c3=student.class;
System.out.println(c3);
//4.基本内置类的包装类都有一个TYPE属性
Class c4=Integer.TYPE;
System.out.println(c4);
//5.获得父类类型
Class c5=c1.getSuperclass();
System.out.println(c5);
}
}
class person{
String name;
public person(){}
public person(String name) {
this.name = name;
}
@Override
public String toString() {
return "person{" +
"name='" + name + '\'' +
'}';
}
}
class student extends person{
public student(){
this.name="hello";
}
}
class teacher extends person{
public teacher(){
this.name="world";
}
}
结果:
这个人是hello
class student
class student
class student
int
class person
哪些类型可以有class对象
ALT可选多行进行复制
import java.lang.annotation.ElementType;
//所有类型的class
public class Test06 {
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;
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];
System.out.println(a.getClass().hashCode());
System.out.println(b.getClass().hashCode());
}
}
结果:
class java.lang.Object
interface java.lang.Comparable
class [Ljava.lang.String;
class [[I
interface java.lang.Override
class java.lang.annotation.ElementType
class java.lang.Integer
void
class java.lang.Class
460141958
460141958
类加载内存分析
类的加载(将类的class文件读入内存,并为之创建一个java.lang.Class对象,此过程由类加载器完成)
->类的链接(将类的二进制数据合并到JRE中:正式为类变量(static)分配内存并设置变量默认初始值阶段,这些内存都将正在方法区中分配)
->类的初始化(JVM负责对类进行初始化:执行类构造器的()方法的过程,编译时自动收集所有类变量的赋值动作和静态代码块中的语句合并产生的)
public class Test07 {
public static void main(String[] args) {
A a=new A();
System.out.println(A.m);
/*
1.加载到内存,会产生一个类对应的class对象
2.链接结束后,m=0(0为int类型默认初始值)
3.初始化,执行类构造器的<clinit>()方法的过程,编译时自动收集所有类变量的赋值动作和静态代码块中的语句合并产生的,
<clinit>{
System.out.println("A类的静态代码块初始化");
m=300;
m=100;}
*/
}
}
class A{
static{
System.out.println("A类的静态代码块初始化");
m=300;
}
static int m=100;
public A(){
System.out.println("A类的无参构造");
}
}
什么时候发生类初始化
1.类的主动引用
-
当虚拟机启动时,先初始化main()方法所在类
-
new一个类的对象
-
调用类的静态成员(除了FINAL)和静态方法
-
使用java.lang.reflect方法对类进行反射调用
-
当初始化一个类,如果其父类没有被初始化,则先会初始化它的父类
public class Test08 { /* 测试类什么时候会初始化 */ static{ System.out.println("main类被加载"); } public static void main(String[] args) { //1.主动引用 son son=new son(); //2.反射也会产生主动引用 try { Class.forName("son"); } catch (ClassNotFoundException e) { e.printStackTrace(); } } } 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; } 结果: main类被加载 父类被加载中... 子类被加载
类的被动引用(不会发生类的初始化)
1.当访问一个静态域时,只有真正声明这个域才会被初始化,例:当通过子类调用父类的静态变量,不会导致子类初始化。
2.通过数组定义类引用,不会触发类的初始化
3.引用常量不会触发此类的初始化(常量在链接阶段就存入调用类的常量池中)
//不会产生类的引用f的方法
System.out.println(son.b);
son[] aray=new son[3];
结果:main类被加载
父类被加载中...
2
类加载器的作用
将class文件字节码内容加载在内存中,并将这些静态数据转换成方法区的运行时的数据结构,然后在堆中生成这个类的java.lang.Class对象,作为方法区中类数据的访问入口。
public class Test09 {
public static void main(String[] args) {
//获得系统类的加载器
ClassLoader c1= ClassLoader.getSystemClassLoader();
System.out.println(c1);
//获取系统类加载器的父类加载器->扩展类加载器
ClassLoader c2= ClassLoader.getSystemClassLoader().getParent();
System.out.println(c2);
//根加载器
ClassLoader c3=c2.getParent();
System.out.println(c3);
//测试当前类是哪个加载器加载的
ClassLoader c4= null;
try {
c4 = Class.forName("Test09").getClassLoader();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
System.out.println(c4);
//如何获得系统类加载器可以加载的路径
System.out.println(System.getProperty("java.class.path"));
//双亲委派机制
//java.lang.String->
/*
C:\Program Files\Java\jdk1.8.0_201\jre\lib\charsets.jar;
C:\Program Files\Java\jdk1.8.0_201\jre\lib\deploy.jar;
C:\Program Files\Java\jdk1.8.0_201\jre\lib\ext\access-bridge-64.jar;
C:\Program Files\Java\jdk1.8.0_201\jre\lib\ext\cldrdata.jar;
C:\Program Files\Java\jdk1.8.0_201\jre\lib\ext\dnsns.jar;
C:\Program Files\Java\jdk1.8.0_201\jre\lib\ext\jaccess.jar;
.....
E:\Project\out\production\注解与反射;
E:\IntelliJ IDEA 2018.1.6\lib\idea_rt.jar
*/
}
}
创建运行时类的对象
例:通过反射获得类的信息
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
//获得类的信息
public class Test10 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException {
User user=new User();
Class s=user.getClass();
//获得包名+类的名字
System.out.println(s.getName());
//类名
System.out.println(s.getSimpleName());
//获得类的属性,
System.out.println("----------");
//getFields()只能找到public属性
Field[] field=s.getFields();
for(Field field1:field){
System.out.println(field1);
}
//可以获得全部属性
System.out.println("----------");
Field[] fields=s.getDeclaredFields();
for(Field field2:fields){
System.out.println(field2);
}
//获得指定属性
Field f=s.getDeclaredField("name");
System.out.println(f);
//获得类的方法,获得本类以及父类的所有方法(公有)
System.out.println("正常的------------");
Method[] methods=s.getMethods();
for (Method method : methods) {
System.out.println(method);
}
System.out.println("=============");
//获得本类的所有方法(包含私有)
Method[] Dmethods=s.getDeclaredMethods();
for (Method dmethod : Dmethods) {
System.out.println(dmethod);
}
//获得指定方法
Method m=s.getMethod("getName");
System.out.println(m);
//有参指定方法
Method m2=s.getMethod("setName", String.class);
System.out.println(m2);
//获得指定构造器
Constructor[] constructors =s.getConstructors();
Constructor[] constructors1=s.getDeclaredConstructors();
System.out.println("普通方法获取的构造器");
for (Constructor constructor : constructors) {
System.out.println(constructor);
}
System.out.println("全部====================");
for (Constructor constructor : constructors1) {
System.out.println(constructor);
}
//获得指定构造方法
Constructor con=s.getConstructor(String.class,int.class,int.class);
System.out.println(con);
}
}
例:通过反射动态的创建对象
1.调用class对象的newInstance()方法
1)类必须有一个无参构造器
2)构造器访问权限要足够
//获得Class对象
Class c= Class.forName("User");
//构建一个对象,本质上是调用了类的无参构造器
User user= (User) c.newInstance();
System.out.println(user);
2.没有无参构造器,只需在操作时明确的调用类中的构造器,并将参数传递进去之后,才能进行实例化操作
①通过Class类的getDeclaredConstructor()取得本类中的指定参数类型的构造器
②向构造器性惨重传递一个对象数组进去,里面包含构造器中所需的各个参数
③通过Constructor实例化对象
//通过构造器创造对象
//获得指定构造方法
Constructor con=c.getConstructor(String.class,int.class,int.class);
User user1= (User) con.newInstance("hello",1,2);
System.out.println(user1);
例:调用指定的方法属性
1.getMethod()获取,设置此方法操作时所需要的参数类型
2.使用Object invoke(Object obj,object[] args)进行调用,并向方法中传递设置的obj对象的参数信息。
注:①Object对应原方法的返回值,无返回值为null
②若原方法为静态方法,此时形参1可为null
③若原方法形参列表为空,形参2为null
④若原方法为private,在调用invoke之前,显式调用method Field Constructor对象都有SetACCessible(TRUE)方法:启动和禁用安全检查的开关
//通过反射调用普通方法
User user2= (User) c.newInstance();
//通过反射获取一个方法
Method method= c.getMethod("setName", String.class);
//激活执行方法invoke(传递对象,参数值)
method.invoke(user2,"hello");
System.out.println(user2.getName());
//通过反射操作属性,不能直接操作私有属性
User user3= (User) c.newInstance();
Field name=c.getDeclaredField("name");
//关掉权限检测,操作属性用set
name.setAccessible(true);
name.set(user3,"world");
System.out.println(user3.getName());
性能分析
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
//分析性能问题
public class Test12 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException {
test1();
test2();
test3();
}
//普通方式调用
public static void test1(){
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 test2() throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
User user = new User();
Class c=user.getClass();
Method m=c.getMethod("getName",null);
long starttime = System.currentTimeMillis();
for (int i = 0; i < 1000000000; i++) {
m.invoke(user,null);
}
long endtime = System.currentTimeMillis();
System.out.println("反射方式执行10亿次需要"+(endtime-starttime)+"ms");
}
//反射方式调用,关闭检测
public static void test3() throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
User user = new User();
Class c=user.getClass();
Method m=c.getMethod("getName",null);
m.setAccessible(true);
long starttime = System.currentTimeMillis();
for (int i = 0; i < 1000000000; i++) {
m.invoke(user,null);
}
long endtime = System.currentTimeMillis();
System.out.println("反射方式,关闭检测执行10亿次需要"+(endtime-starttime)+"ms");
}
}
结果:普通方式执行10亿次需要60ms
反射方式执行10亿次需要6719ms
反射方式,关闭检测执行10亿次需要2345ms
获取注解信息
import java.lang.annotation.*;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Map;
//练习反射操作注解
public class Test13 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
Class c1= Class.forName("student2");
//获得注解
Annotation[] ann=c1.getAnnotations();
for (Annotation annotation : ann) {
System.out.println(annotation);
}
//获得指定注解的value值
Table annotation=(Table)c1.getAnnotation(Table.class);
String value= annotation.value();
System.out.println(value);
//获得类属性指定的注解
Field f=c1.getDeclaredField("name");
Fieldde s=f.getAnnotation(Fieldde.class);
System.out.println(s.columnName());
System.out.println(s.type());
System.out.println(s.length());
}
}
@Table("db_student")
class student2{
@Fieldde(columnName = "id",type="int",length=10)
private int id;
@Fieldde(columnName = "age",type="int",length=10)
private int age;
@Fieldde(columnName = "name",type="varchar",length=3)
private String name;
public student2() {
}
public student2(int id, int age, String name) {
this.id = id;
this.age = age;
this.name = name;
}
public int getId() {
return id;
}
public int getAge() {
return age;
}
public String getName() {
return name;
}
public void setId(int id) {
this.id = id;
}
public void setAge(int age) {
this.age = age;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "student2{" +
"id=" + id +
", age=" + age +
", name='" + name + '\'' +
'}';
}
}
//类名的注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface Table{
String value();
}
//属性的注解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface Fieldde{
String columnName();
String type();
int length();
}
结果:@Table(value=db_student)
db_student
name
varchar
3