注解
Annotation 是从JDK5.0开始引入的技术。
内置注解(在java.lang包中)
- @Override,方法重写。
- @Deprecated(已被淘汰),可用于修饰类、属性、方法。不推荐程序员使用这样的元素,因为它很危险或者已经存在更好的选择。
- @SuppressWarnings:抑制编译时的警告信息,需要添加参数才能正常使用。比如:@SuppressWarnings("all")
元注解
负责注解其他注解,java定义了4个标准的meta-annotation类型。
- @Target,描述注解的使用范围
- @Retention,描述注解的生命周期
- SOURCE < CLASS < RUNTIME
- @Document,表示该注解将被包含在 javadoc中
- Inherited,说明子类可以继承父类中的该注解
自定义注解
@interface + 注解名{ 参数类型 + 参数名(); }
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface DIY {
String name() default "";
int age();
}
反射机制(Java.Reflection)
反射机制允许程序在运行期间,借助反射API获取任何类的内部信息,并且能直接操作任意对象的内部属性及方法。
Class c = Class.forName("java.lang.String");
类加载完之后,在堆内存的方法区中就产生了一个Class类对象(Class类是用来描述类的类,一个类只有一个Class类对象),这个Class类对象包含了完整的类的结构信息。
反射的功能
- 运行时判断任意一个对象所属的类
- 运行时判断任意一个类所具有的属性和方法
- 运行时构造任意一个类的对象
- 运行时获取泛型信息
- 运行时调用任意一个对象的属性和方法
- 运行时处理注解
- 生成动态代理
反射优点:
- 可以动态创建对象,灵活性很强
缺:
- 对性能有影响,执行速度比较慢
反射相关的主要API
API | Description |
---|---|
java.lang.Class | 代表一个类 |
java.lang.reflect.Method | 代表类的方法 |
java.lang.reflect.Field | 代表类的属性 |
java.lang.reflect.Constructor | 代表类的构造器 |
获取Class对象的方式
//1.通过全限定类名获得
Class<?> c1 = Class.forName("reflect.User");
//2.通过对象获得
Class c2 = new User().getClass();
//3.通过类名.class获得
Class c3 = User.class;
所有类型的class
// 所有类型的class:
Class<Object> objectClass = Object.class;
Class<Comparable> comparableClass = Comparable.class;//接口
Class<String[]> aClass = String[].class;
Class<String[][]> aClass1 = String[][].class;
Class<Override> overrideClass = Override.class;//注解
Class<ElementType> elementTypeClass = ElementType.class;//枚举
Class<Integer> integerClass = Integer.class;
Class<Void> voidClass = void.class;
Class<Class> classClass = Class.class;
Class<Double> doubleClass = Double.class;
Class<Float> floatClass = Float.class;
Class<Boolean> booleanClass = Boolean.class;
java内存分析
- 堆:
- 存放new的对象和数组
- 可以被所有线程共享,不会存放别的对象的引用
- 栈
- 存放基本数据类型
- 存放引用对象的地址(引用在堆里面的具体地址)
- 方法区(是一个特殊的堆)
- 包含了所有的class和static变量
- 可以被所有线程共享
类加载的过程
当程序主动使用某个类时,如果该类还没有被加载到内存中,系统就会通过以下三个步骤对该类进行初始化。
- 类的加载:
- 将类的class字节码文件加载到内存中,把静态数据转换成方法区的运行时数据,然后创建Class对象。这个过程由类加载器完成。
- 类的链接:
- 将类的二进制数据合并到jre中
- 验证:确保加载的类符合JVM规范,不存在安全问题
- 准备:为类变量分配内存并设置初始值,这些内存都在方法区中分配
- 解析:虚拟机常量池内的常量名,替换为地址。
- 将类的二进制数据合并到jre中
- 类的初始化:
- JVM负责对类进行初始化
- 执行类构造器
<clinit>()
方法,负责构造类信息,不是构造该类的构造器 - 初始化一个类的时候,如果发现父类还没有进行初始化,会先对父类进行初始化
- JVM会保证一个类的
<clinit>()
方法在多线程环境中被正确加锁同步
- 执行类构造器
- JVM负责对类进行初始化
类加载器
类加载器的作用:把类的class字节码文件加载到内存中,并且把静态数据转换成方法区的运行时数据,然后创建Class对象,作为方法区中类数据的访问入口。
- 类引导加载器:
- C++编写的,JVM自带的,负责装载java平台核心库(rt .jar)。该加载器无法直接获取。
- 类扩展加载器:
- 负责装载jre/lib。。下的jar包。
- 系统类加载器:
- 负责java.class.path下的类和jar包的加载,是最常用的加载器
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
ClassLoader parent = systemClassLoader.getParent();
ClassLoader parent1 = parent.getParent();
获取当前类加载器:是最常用的系统类加载器
//获取当前类的类加载器
ClassLoader classLoader = Class.forName("reflect.Reflect").getClassLoader();
System.out.println(classLoader);
获取类名:
//获得类的名字
System.out.println(aClass);//class + 包名+类名
System.out.println(aClass.getName());//包名+类名
System.out.println(aClass.getSimpleName());//类名
获取属性:
//获得类的属性
System.out.println("============");
//getFields()只能获取public属性
Field[] field = aClass.getFields();
for (Field pub:field) {
System.out.println(pub);
}
//getDeclaredFields()获取所有的属性
System.out.println("============");
Field[] fields = aClass.getDeclaredFields();
for (Field f:fields) {
System.out.println(f);
}
获取方法:
//获取该类的public方法和父类的public方法
Method[] methods = aClass.getMethods();
//获取该类的所有方法
Method[] declaredMethods = aClass.getDeclaredMethods();
获取构造器:
//获取该类的public构造器
Constructor<?>[] constructors = aClass.getConstructors();
//获取该类的所有构造器
Constructor<?>[] declaredConstructors = aClass.getDeclaredConstructors();
通过反射动态创建对象
有了Class类对象后,可以调用Class类对象的newInstance()方法,创建对象
- 类必须有一个无参构造器
- 类的构造器有足够的访问权限
//通过无参构造创建对象
Object o = aClass.newInstance();
//通过构造器创建对象
Constructor c = aClass.getDeclaredConstructor(String.class,int.class);
Object newInstance = c.newInstance("柯柯", 18);
通过反射操作方法:
//通过反射操作方法
Method setSss = aClass.getDeclaredMethod("setSss", String.class);
setSss.invoke(newInstance,"kkkkkk");//传入对象 + 参数值
System.out.println(newInstance);
通过反射操作属性:
//可以直接操作类的public属性
Field sss = aClass.getDeclaredField("sss");
sss.set(newInstance,"qqqqq");
System.out.println(newInstance);
//不能直接操作类的私有属性,需要关闭安全检测
Field name = aClass.getDeclaredField("name");
//设置为可进入的,可使用的,默认为false
name.setAccessible(true);
name.set(newInstance,"www");
System.out.println(newInstance);
通过反射获取注解:
Class<?> aClass = Class.forName("reflect.User");
// 通过过反射获取注解
Annotation[] annotations = aClass.getDeclaredAnnotations();
for (Annotation annotation: annotations) {
System.out.println(annotation);
}
// 通过过反射获取注解的值
DIY diy = aClass.getDeclaredAnnotation(DIY.class);
System.out.println(diy.value());
// 获得属性的注解
Field name = aClass.getDeclaredField("name");
DIYField diyField = name.getAnnotation(DIYField.class);
System.out.println(diyField.value());
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface DIY{
String value();
}
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface DIYField{
String value();
}