内置注解
– @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为类的实例
注意:
- 获取类对象的名字
Class.forName().getName() //获得包名+类名(全限定名)
- 简单名字
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()方法 :合并所有赋值动作
注意:
- 执行顺序:静态代码块>类构造器>其他
- 当初始化一个类,若其父类没有被初始化,则先会初始化它的父类
- 类的字节码加载到内存后,会生成此类的一个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();
注意:
- 获取当前类加载器:
Class.forName().getClassLoader()
- 获取系统类加载器可以加载的路径:
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
}