笔记之【注解和反射】

内置注解

– @Override:重写
– @Deprecated:过时
– @SupressWarnings:抑制警告

元注解(定义注解的注解)

表示用在何处(方法上、类上)
@Target (value ={ElementType.METHOD,ElementType.TYPE})

作用范围(runtime>class>sources)
@Retention(value=RetentionPolicy.RUNTIME)

表示是否将我们的注解生成在JAVAdoc中
– @Documented

子类可以继承父类的注解
– @Inherited

自定义注解

使用@interface定义,用元注解限定,代码如下:

@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@ interface MyAnnotation{
	//参数类型+参数名+();
	String name();
	String name() default ""; //可以设置默认值
	int age();
}

反射

public class Test {
    public static void main(String[] args) throws ClassNotFoundException {
        Class c1 = Class.forName("com.zane.reflection.User");
        System.out.println(c1);
    }
    
}

public class User{...}

– 通过Class.forName(“包.类”) 获取反射对象
– 即使多次反射,只会获得一个class对象(类的整个结构被封装在class对象中)

获取class类的实例(Class对象)

– 1. Class.forName()
– 2. Person.class //该方法最为安全可靠
– 3. person.getClass()
– 4. 获取对象的父类 c1.getSuperclass(); //c1为类的实例

注意:

  1. 获取类对象的名字
Class.forName().getName()		//获得包名+类名(全限定名)
  1. 简单名字
Class.forName().getSimpleName()	//获得类名

哪些类型有class对象

上小节的2:Person.class方式
calss、interface、[ ]、enum、annotation、primitive type、void

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;			//Class

类加载机制

  • 加载
    字节码内容加载到内存,产生一个类对应class对象
  • 链接
    – 验证
    – 准备:为类变量(static)分配内存并附默认初始值
    – 解析
  • 初始化(详见下小节)
    clinit()方法 :合并所有赋值动作

注意:

  1. 执行顺序:静态代码块>类构造器>其他
  2. 当初始化一个类,若其父类没有被初始化,则先会初始化它的父类
  3. 类的字节码加载到内存后,会生成此类的一个java.lang.Class对象(Class对象唯一,无论有多少个类的对象

类初始化

  • 类的主动引用(一定会发生类的初始化)
    – 通过new
    – 通过反射:Class.forName()
    – 调用类的静态成员/静态方法
    – 当初始化一个类,若其父类没有被初始化,则先会初始化它的父类

  • 类的被动引用(不会发生类的初始化)
    – 通过子类引用父类的静态变量时,不会导致子类初始化(不会加载子类)
    – 通过数组定义类引用,不会触发此类初始化
    引用类的常量不会初始化此类

//静态入口类中:
System.out.println(Son.b)		//引用父类静态变量,此时不会加载并初始化子类,即子类的静态代码块不会被执行

Son[] arr = new Son[5]		//定义数组类引用,此时不会加载并初始化子类,即子类的静态代码块不会被执行

System.out.println(Son.M)		//引用常量,此时不会加载并初始化子类,即子类的静态代码块不会被执行


//父类:
class Father{
	//静态变量
	static int b=2;
	
	//静态代码块
	static{
		System.out.println("父类被加载")
	}
}

//子类:
class Son extnds Father{

	//静态代码块
	static{
		System.out.println("子类被加载")
	}
	//常量
	static final int M =1;
}

三级类加载器

  • 根加载器
Classloader pParent= parent.getParent();
  • 扩展类加载器
Classloader parent= sysLoader.getParent();
  • 系统类加载器(常用)
Classloader sysLoader = Classloader.getSystemClass();

注意:

  1. 获取当前类加载器:
Class.forName().getClassLoader()
  1. 获取系统类加载器可以加载的路径:
System.getProperty("java.class.path")

获得类的信息(运行时结构)

Class c1 = Class.foeName

  • 获取类名
c1.getName();		//获得包名+类名(全限定名)
c1.getSimpleName();		//获得类名
  • 获得类的属性
Field[] fields = c1.getFields();			//只能找到public属性
Field[] fields = c1.getDeclaredFields();	//全部属性

Field[] field = c1.getDeclaredField("属性");	//获得指定属性
  • 获得类的方法
Method[] methods = c1.getMethods();			//获得本类及父类的全部public方法
Method[] methods = c1.getDeclaredMethods();	//本类所有方法

Method[] method = c1.getMethod("方法",类型);	//获得指定方法
  • 获得类的构造器
Constructor[] constructors = c1.getConstructors();			//获得public构造方法
Constructor[] constructors = c1.getDeclaredConstructors();	//本类所有构造方法

Constructor[] declaredConstructor = c1.getDeclaredConstructor(类型.class,类型.class);	//获得指定构造器

动态创建对象+调用方法

先通过反射得到class对象c1

  • 创建对象
    1. c1.newInstance() //通过class对象实例出来一个对象(调用无参构造器)
    2. c1.getDeclaredConstructor(类型1.class,类型2.class) .newInstance(传参1,传参2) //通过构造器传参创建对象,并传参(需要显式有参构造器)
Class c1 = Class.forName("com.zane.reflection.User");

//1.通过class对象实例出来一个对象(调用无参构造器)
User user1 = (User)c1.newInstance();
System.out.println(user1);

//2.通过构造器传参创建对象
Constructor constructor = c1.getDeclaredConstructor(String.class, int.class);	//获得构造器
User user2 = (User) constructor.newInstance("zhangsan", 28);					//传参
System.out.println(user2);
  • 调用方法
    实例对象+获得方法+invoke绑定
//调用方法
User user3 = (User)c1.newInstance();    //先实例出一个对象
Method setName = c1.getDeclaredMethod("setName",String.class);  //获取指定方法(需要隐去有参构造器)
setName.invoke(user3,"lisi");   //invoke方法,绑定对象并传参
System.out.println(user3.getName());
  • 操作属性
    实例对象+获得属性+开启权限+set绑定
//操作属性
User user4 = (User)c1.newInstance();    //先实例出一个对象
Field name = c1.getDeclaredField("name");   //获得指定属性

name.setAccessible(true);       //开启访问private的权限(方法、字段、构造器都有setAccessible方法)
name.set(user4,"wangwu");       //set方法,绑定并传参
System.out.println(user4.getName());

测试性能

//普通方式
public static void test01(){
    User user = new User();                     //创建对象
    long start = System.currentTimeMillis();
    for (int i=0;i<1000000000;i++){
        user.getName();                         //直接调用
    }
    long end = System.currentTimeMillis();
    System.out.println("普通:"+(end-start)+"ms");
}

//反射方式
public static void test02() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
    User user = new User();
    Class c1 = user.getClass();                 //反射出class对象
    Method getName = c1.getDeclaredMethod("getName", null);
    long start = System.currentTimeMillis();
    for (int i=0;i<1000000000;i++){
        getName.invoke(user,null);       //反射调用方法
    }
    long end = System.currentTimeMillis();
    System.out.println("反射:"+(end-start)+"ms");
}

//反射方式+开启权限
public static void test03() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
    User user = new User();
    Class c1 = user.getClass();                 //反射出class对象
    Method getName = c1.getDeclaredMethod("getName", null);

    getName.setAccessible(true);                //开启访问private权限
    long start = System.currentTimeMillis();
    for (int i=0;i<1000000000;i++){
        getName.invoke(user,null);       //反射调用方法
    }
    long end = System.currentTimeMillis();
    System.out.println("反射+访问private权限:"+(end-start)+"ms");
}

public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
    test01();	//普通:4ms
    test02();	//反射:3389ms
    test03();	//反射+访问private权限:1740ms
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值