一、注解 Annotation
注解是从JDK5.0开始的技术,其作用有:
注解不是程序,它能对程序作出解释,类似注释
注解可被其他程序如编译器读取
注解还可做一些代码检查
注解的格式:
@注释名 还可添加参数值,如
@SuppressWarnings(value="unchecked")
注解使用的地方:
可在package,class,method,filed等上面,给他们添加了额外的信息,我们可通过反射机制实现对这些元数据的访问
内置注解
@override :定义在java.lang.Override中,此注释只用于修辞手法,表示一个方法声明重写超类中的另一个方法声明
@Deprecated:定义在java.lang.Deprecated中,此注释可用于修辞手法,属性、类,表示不鼓励程序员使用这样的元素,通常因为很危险或存在更好的选择
@SuppressWarnings:定义在java.lang.SuppressWarnings中,用来抑制编译时的警告信息,此注解需要添加参数才能使用,可用参数如下:
@SuppressWarnings(“all”)
@SuppressWarnings(“unchecked”)
@SuppressWarnings(value={“unchecked”,“deprecation”)等等…
元注解
元注解作用是负责注解其他的注解,java定义了4个标准的meta-annotation类型,它们被用来提供对其他注解类型做说明
可在java.lang.annotation中找到它们:
@Target:用于描述注解的使用范围
@Retention:表示需要在什么级别保存该注释信息,用于描述注解的声明周期
(SOURCE<CLASS<RUNTIME)
SOURCE表示只在源文件中存在,编译后就抛弃了,CLASS运行时不存在,RUNTIME表示加载进内存中
@Documented:说明该注解将被包含在javadoc中
@Inherited:说明子类可以继承父类的该注解
自定义注解
-
用@interface定义注解,将自动继承Annotation接口
-
结合元注解定义新注解
下面定义一个在方法和类、运行时有效、可生成到javadoc中、并且可被继承的注解
@Target(value = {ElementType.TYPE,ElementType.METHOD})
@Retention(value = RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface MyAnnotation {
// 注解的参数格式 参数类型 参数名();
// 这里不是方法,而是参数,有默认值时,可以不用写
String value() default "";
}
@MyAnnotation("a")
public void Ab(){}
二 反射 Reflection
学反射之前先了解一下静态和动态语言
动态语言
是一类在运行时可以改变其结构的语言,如新的函数、对象甚至代码可以被引进,已有的函数可以被删除或者其他结构上发生变化,。通俗来讲就是在运行时代码可以根据一些条件改变自身结构。主要动态语言有:Object-C,C#,JavaScript,PHP,Python等。
静态语言
与之对应,运行时不发生变化的语言,如java,C,C++.
java不是动态语言,但可以称为准动态语言,我们可以利用反射机制获得类似动态语言的特性,java得动态性使得编程时更加灵活。
Reflection:是java被视为动态语言的关键,反射机制允许程序在执行期借助于REflectionAPI取得任何类的内部信息,并且能直接操作任何对象的内部属性和方法。
class c = Class.forName("java.lang.String");
加载类之后,在堆内存
的方法区就产生了一个Class类型的对象,一个类只有一个Class对象,这个对象包含了完整的类的结构信息,就像一面镜子,我们可以通过这个对象看到这个类的结构,因此我们称之为反射。
反射的功能
- 在运行时判断任意一个对象所属的类
- 在运行时构造任意一个类的对象
- 在运行时判断任意一个类所具有的成员变量和方法
- 在运行时获取泛型信息
- 在运行时调用任一个对象的成员变量和方法
- 在运行时处理注解
- 生成动态代理…
反射的优缺点
优点:可以实现动态创建对象和编译,体现出灵活性
缺点:对性能有影响,使用反射基本上是一种解释性操作,我们可以告诉JVM我们希望做什么并且让他满足我们的要求,这类操作总是慢于直接执行的操作。
主要API
Class类
类也是对象
在Object类中定义了一个方法,被所有子类继承
public final Class getClass()
该方法返回类型是一个Class类,此类为java反射的源头,实际上所谓反射从程序的运行结构看就是通过对象反射求出类的名称。
一个类的Class对象在类加载后产生,存在堆中
获取class类的实例
package reflection;
public class MyFlection {
public static void main(String[] args) throws ClassNotFoundException {
Person p1 = new Student();
System.out.println("这个人是"+p1.name);
// 通过对象获取
Class c1 = p1.getClass();
System.out.println(c1.hashCode());
// forName获得
Class c2 = Class.forName("reflection.Student");
System.out.println(c2.hashCode());
// 通过类名获得
Class c3 = Student.class;
System.out.println(c3.hashCode());
// 基本类型的包装类的type属性
Class type = Integer.TYPE;
System.out.println(type);
// 获得父类型
Class c4 = c1.getSuperclass();
System.out.println(c4);
}
}
class Person{
String name;
public Person() {
}
}
class Student extends Person{
public Student() {
this.name = "学生";
}
}
class Teacher extends Person{
public Teacher() {
this.name = "教师";
}
}
哪些类可以有class对象
Class c1 = Object.class; // 类 class java.lang.Object
Class c2 = Comparable.class; //接口 interface java.lang.Comparable
Class c3 = String[].class; // class [Ljava.lang.String;
Class c4 = int[][].class; //class [[I
Class c5 = Override.class; //注解 //interface java.lang.Override
Class c6 = ElementType.class; //枚举 class java.lang.annotation.ElementType
Class c7 = Integer.class;//class java.lang.Integer
Class c8 = void.class;//void
Class c9 = Class.class; //class class java.lang.Class
类加载过程
类的初始化
什么时候会发生类的初始化:
类的主动引用(一定会发生类的初始化)
- 当虚拟机启动时,先初始化main方法所在的类
- new 一个类的对象
- 调用类的静态成员和静态方法(除了final常量)
- 使用java.lang.reflect包的方法堆类进行反射调用
- 初始化一个类,弱国其父类没有被初始化,先初始化其父类
- 反射也会产生主动引用
类的被动引用(不会发生类的初始化)
- 当访问一个静态域时,只有真正声明这个域的类才会被初始化,如通过子类引用父类的静态变量,不会导致子类初始化
- 通过数组定义类引用,不会触发此类的初始化
- 引用常量不会触发此类的初始化(常量在链接阶段就存入调用类的常量池中了)
public class MyTest {
public MyTest() {
System.out.println("main类被加载");
}
public static void main(String[] args) throws ClassNotFoundException {
System.out.println(A.m); //10
Class aClass = Class.forName("reflection.A"); // A 类静态域被加载
A[] arr = new A[10];// 通过数组定义类引用,不会触发此类的初始化
}
}
class A{
public static final int m = 10;
static {
System.out.println(" A 类静态域被加载");
}
public A() {
System.out.println("A类加载");
}
}
类加载器的作用
- 将class字节码文件加载到内存中,并将这些静态数据转换成方法区的运行时数据结构,然后在堆中生成一个代表这个类的java.lang.Class对象,作为方法区中类数据的访问入口
- 类缓存:标准的javaSE类加载器可以按要求查找类,但一旦某个类被加载到类加载器中,它将维持加载(缓存)一段时间,不过JVM垃圾回收机制可以回收这些Class对象
java核心类库:rt.jar
类加载器测试
// 获取系统类的加载器
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
System.out.println(systemClassLoader); //sun.misc.Launcher$AppClassLoader@18b4aac2
// 获取系统类加载器的父类-->扩展类加载器
ClassLoader parent = systemClassLoader.getParent();
System.out.println(parent); //sun.misc.Launcher$ExtClassLoader@4554617c
// 获取扩展类加载器的父类加载器-->跟加载器(由C/C++编写的,无法直接获取)
ClassLoader parent1 = parent.getParent();
System.out.println(parent1); //null 即根加载器
// 测试当前类是那个类加载器加载的
ClassLoader classLoader = Class.forName("reflection.MyTest").getClassLoader();
System.out.println(classLoader); //sun.misc.Launcher$AppClassLoader@18b4aac2
// 看JDK内置的类是谁加载的
classLoader = Class.forName("java.lang.Thread").getClassLoader();
System.out.println(classLoader); //null
// 获得系统类加载器可加载的路径
System.out.println(System.getProperty("java.class.path"));
反射获取运行时类的完整结构包括继承的父类、实现的接口、注解等信息
Class c1 = Class.forName("reflection.User");
// 获取类名
String name = c1.getName();
System.out.println(name);
// 获得类属性
Field[] fields = c1.getFields(); // 只能获得公有的
for (Field field : fields) {
System.out.println(field);
}
fields = c1.getDeclaredFields(); // 可获得私有属性
for (Field field : fields) {
System.out.println(field);
}
System.out.println("========================================");
// 获得类方法
Method[] methods = c1.getMethods(); //获得所有公有方法,包括父类的
for (Method method : methods) {
System.out.println("getMethods:"+method);
}
methods = c1.getDeclaredMethods(); //获得本类的所有方法,包括私有的
for (Method method : methods) {
System.out.println("getDeclaredMethods:"+method);
}
System.out.println("========================================");
// 获得指定方法 getDeclaredMethod可获取私有的 需要参数,防止方法重载而找不到方法
// getMethod 只能获取公有的
Method getName = c1.getDeclaredMethod("getName", null);
System.out.println(getName);//private java.lang.String reflection.User.getName()
Method setAge = c1.getMethod("setAge", int.class);
System.out.println(setAge);//public void reflection.User.setAge(int)
Method setName = c1.getMethod("setName", String.class);
System.out.println(setName);//public void reflection.User.setName(java.lang.String)
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("#"+constructor);
}
// 获取指定构造器 指定参数
Constructor constructor = c1.getConstructor();
System.out.println("指定:"+constructor);
setAccessible
下面是测试结果:执行单一实体类的getAge()方法
执行10亿次所用时间:8ms 正常执行
执行10亿次所用时间:4053ms 反射执行
执行10亿次所用时间:2912ms 反射执行关闭检测
反射操作泛型
反射操作注解
ORM:Object relationship Mapping 对象关系映射
@MyAnno1("db_student")
public class Re_Annotation {
@MyAnno2(colName = "年龄",type = "int",length = 10)
private int age;
@MyAnno2(colName = "姓名",type = "String",length = 20)
private String name;
public static void main(String[] args) throws NoSuchFieldException {
Class c1 = Re_Annotation.class;
// 通过反射获取注解
Annotation[] annotations = c1.getAnnotations();
for (Annotation annotation : annotations) {
System.out.println(annotation); //@reflection.MyAnno1(value=db_student)
}
// 获取注解,并打印其中的值, value()方法
MyAnno1 annotation = (MyAnno1) c1.getAnnotation(MyAnno1.class);
System.out.println(annotation.value()); //db_student
// 获取指定注解 先获取属性,再根据属性获取注解
Field field = c1.getDeclaredField("age");
MyAnno2 annotation1 = field.getAnnotation(MyAnno2.class);
System.out.println(annotation1.colName()); //年龄
System.out.println(annotation1.type()); //int
System.out.println(annotation1.length()); //10
}
}
@Target(ElementType.TYPE)// 类的注解
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnno1{
String value();
}
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnno2{
String colName();
String type();
int length();
}
了解点
双亲委派机制,看这个容易理解
附上链接秦时的明月夜