注解Annotation是从JDK5.0开始引入的新技术。
作用:
不影响程序本身,但是可以对程序作出解释可以被其他程序(如编译器等)读取Annotation的格式:注解是以"@注释名"再代码中存在,还可以添加一些参数值,如@SuppressWarnings(value = “unchecked”)
注解的使用场景:可以附加在package,class,method,field等上面,相当于是添加了辅助信息,也可以通过反射机制编程实现对元数据的访问
内置注解@Override:定义在java.lang包中,此注释只适用于修饰方法,表示一个方法声明打算重写超类中的另外一个方法声明@Depercated:定义在java.lang包中,此注释用于修饰方法、属性、类,表示所定义的东西不鼓励使用,因为其很危险或者有其他更好的选,当使用所修饰的这个方法时,方法中间会带有一条划掉的横线@SuppressWarnings:定义在java.lang包中,用来抑制编译时的警告信息,与前两个不同,这个需要在括号中添加参数才能正确使用,如:@SuppressWarnings(“all”); 元注解
元注解的作用就是负责注解其他注解,Java定义了4个标准的meta-annotation类型,他们被用来提供对其他annotation类型说明这些类型和他们所支持的类在java.lang包中可以找到,如@Taget,@Retention,@documented,@Inherited
@Taget:用于描述注解的使用范围(即:被描述的注解可以用在说明地方)@Retention:表示需要在什么级别保存该注释信息,用于描述注解的生命周期(SOURCE(源码里面) 使用@interface自定义注解时,自动继承java.lang.annotation.Annotation接口 分析: @interface用来声明一个注解,格式:public @interface 注解名{定义内容} 其中的每个方法实际上是声明一个配置参数 方法名称就是参数名称 返回值类型就是参数的类型(返回值只能是基本类型、Class、String、enum) 可以通过default来声明参数的默认值 如果只有一个参数成员,一般参数名为value 注解元素必须要有值,我们定义注解元素时,经常使用空字符串,0作为默认值 测试代码如下: import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;//自定义注解测试public class Test02 { //没有赋予默认值的参数要在使用注解时给注解赋值 @MyAnnotation02(name = "你好",a = 20) public void method01(){} @MyAnnotation03(value = "你好") public void method02(){} }@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)@interface MyAnnotation02{ //注解的参数:参数类型 + 参数名(); String name(); int a(); //注解也可赋予默认值。则在使用的时候就不用输入默认值 int id() default 100;//默认值为100 String[] arr() default {"小明","小冷"};//默认值为"小明","小冷"}@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)@interface MyAnnotation03{ //注解的参数:参数类型 + 参数名(); String value();//只有一个参数时,参数名最好用value} Reflection(反射)是Java被视为动态语言的关键,反射机制允许程序在执行期借助于Reflection API取得任何类的内部信息,并且直接操作任意对象的内部属性和方法:Class c = Class.forName(“java.lang.String”)加载完类之后,在堆内存的方法区中产生了一个Class类型的对象(一个类只有Class对象),这个对象就包含了完整类的结构信息,通过这个对象来看到类的结构,所以称之为“反射”。 优点:可以实现动态创建对象和编译,体现出很大的灵活性 缺点:对性能有影响,使用反射基本上是一种解释操作,可以告诉jvm我们的需求,但是这类操作总是慢于直接执行相同的操作。 java.lang.Class:代表一个类java.lang.reflect.Method:代表类的方法java.lang.reflect.Field:代表类的成员变量java.lang.reflect.Constructor:代表类的构造器 以上方法返回值类型都是一个Class类,此类是java反射的源头,,所以也可以看作是通过对象求出类的名称。 public class Test01 { public static void main(String[] args) throws ClassNotFoundException { //通过反射来获取类的class对象 Class c1 = Class.forName("注解和反射.反射.User"); System.out.println(c1); System.out.println(c1.getAnnotations());//获取类的注解 System.out.println(c1.getConstructors());//获取类的方法 //创建多个对象,通过hashcode方法判断是否是同一个类 Class c2 = Class.forName("注解和反射.反射.User"); Class c3 = Class.forName("注解和反射.反射.User"); //一个类在内存中只有一个Class对象 //一个类被加载之后,整个类都会被封装在Class对象之中 System.out.println(c1.hashCode()); System.out.println(c2.hashCode()); System.out.println(c3.hashCode()); //发现三个对象的类都是一样的 }}//测试类class User{} 结果如下: //测试Class类的创建方式有哪些public class Test02 { public static void main(String[] args) throws ClassNotFoundException { Person person = new Student(); System.out.println("这个人是" + person.name); //方式一:通过对象获取 Class c1 = person.getClass(); //方式二:用forname获取 Class c2 = Class.forName("注解和反射.反射.Test02"); //方式三:通过类名.class获取 Class c3 = Student.class; //判断是否是同一个类 System.out.println(c1.hashCode()); System.out.println(c2.hashCode()); System.out.println(c3.hashCode()); //方式四:基本内置类型的包装类都有一个Type属性 Class c4 = Integer.TYPE;//输出的是int类 System.out.println(c4); //获取父类类型的方法 Class c5 = c1.getSuperclass(); System.out.println(c5); }}//测试类class Person{}class Student extends Person{ public Student (){ this.name = "学生"; }} 运行出的结果如下: import java.lang.annotation.ElementType;//所有类型的Classpublic class Test03 { public static void main(String[] args) { Class c1 = Object.class;//类 Class c2 = Comparable.class;//接口 Class c3 = String[].class;//一维数组 Class c4 = Override.class;//注解 Class c5 = ElementType.class;//枚举 Class c6 = void.class;//void空类型 Class c7 = Class.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); }} 运行结果如下: 堆内存: 存放new的对象和数组可以被所有线程共享,不会存放别的对象的引用 栈内存: 存放基本变量类型(会包含这个基本类型的具体数值) 引用对象的变量(会存放这个引用在堆内存的具体地址) 方法区: 可以被所有线程共享包含所有的class和static变量 类的加载过程 当程序使用某类时,如果该类还未被加载到内存中,则系统会通过如下三步来对类进行初始化 验证:确保加载的类的信息符合JVM规范,没有安全方面的问题准备:正式为类变量(static)分配内存并设置类变量的默认初始值的阶段,这些内存都将在方法区中进行分配解析:虚拟机常量池内的符号引用(常量名)替换为直接引用(地址)的过程 类的初始化(Initialize):JVM负责对类进行初始化。 执行类构造器()方法的过程。类构造器()方法是由编译期自动收集类的所有类变量的赋值动作和静态代码块中的语句合并产生的(类构造器是构造类信息的,表示构造该类对象的构造器)当初始化一个类的时候,如果发现其父类还没有进行初始化,则需要先触发其父类的初始化虚拟机会保证一个类的()方法在多线程环境中被正确加锁和同步 eg: public class Test04 { public static void main(String[] args) { A a = new A(); System.out.println(A.m);//输出m是100 }}class A{ static { System.out.println("A类静态代码块初始化"); m = 300; } static int m = 100; public A(){ System.out.println("A类的构造器初始化"); }} 结果如下: public class Test05 { //1.1当虚拟机启动时,先初始化main方法所在的类 static { System.out.println("主类被加载"); } public static void main(String[] args) throws ClassNotFoundException { //1.2new一个类对象 //Son son = new Son(); //1.3调用类的静态成员(除了final常量)和静态方法 //System.out.println(Son.m); //1.4使用java.lang.reflect包的方法对类进行反射调用 //Class.forName("注解和反射.反射.Test05"); //2.1当通过子类引用父类的静态变量,不会导致子类初始化 //System.out.println(Son.b); //2.2通过数组定义类的引用,不会触发此类的初始化 //Son[] arr = new Son[5]; //2.3引用常量不会触发此类的初始化 //System.out.println(Son.M); }}class Father{ static int b = 2; static { System.out.println("父类被加载"); }}class Son extends Father{ static { System.out.println("子类被加载"); m = 300; } static int m = 100; static final int M = 1;} 类似于获取类的属性: import java.lang.reflect.Constructor;import java.lang.reflect.Field;import java.lang.reflect.Method;public class Test06 { public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException { //第一种方式 Class c1 = Class.forName("注解和反射.反射.User"); //第二种方式 User user = new User(); c1 = user.getClass(); //获取类的名字 System.out.println(c1.getName());//获得包名加类名 System.out.println(c1.getSimpleName());//获得类名 System.out.println("============================="); //获得类的属性 //只能获取类的public属性 Field[] fields = c1.getFields(); for (int i = 0; i < fields.length; i++) { System.out.println(fields[i]); } //可以获取类的全部属性 fields = c1.getDeclaredFields(); for (int i = 0; i < fields.length; i++) { System.out.println(fields[i]); } //获取指定属性 Field name = c1.getField("name"); System.out.println(name); System.out.println("============================="); //获取类的方法 //获取本类以及父类的全部public方法 Method[] methods = c1.getMethods(); for (Method method : methods) { System.out.println("第一种方法" + method); } //只获得本类的所有方法 Method[] declaredMethods = c1.getDeclaredMethods(); for (Method declaredMethod : declaredMethods) { System.out.println("第二种方法" + declaredMethod); } //获得指定方法 Method getName = c1.getMethod("getName", null); System.out.println(getName); Method setName = c1.getMethod("setName", String.class); System.out.println(setName); System.out.println("============================="); //获得指定构造器 //只能获取类的public构造器 Constructor[] constructors = c1.getConstructors(); for (Constructor constructor : constructors) { System.out.println(constructor); } //能获取类的全部构造器 Constructor[] declaredConstructors = c1.getDeclaredConstructors(); for (Constructor declaredConstructor : declaredConstructors) { System.out.println(declaredConstructor); } //获取指定的构造器 Constructor declaredConstructor = c1.getDeclaredConstructor(String.class); System.out.println(declaredConstructor); }} 1.创建类的对象:调用Class对象的**newInstance()**方法 2.若没有无参构造器如何创建对象 3.调用指定的方法 4.调用指定的属性 代码实现: import java.lang.reflect.Constructor;import java.lang.reflect.Field;import java.lang.reflect.Method;//通过反射,动态创建对象public class Test07 { public static void main(String[] args) throws Exception{ //获取Class对象 Class c1 = Class.forName("注解和反射.反射.User"); //1.构造一个对象(通过无参构造器) User user = (User) c1.newInstance();//本质上是调用类的无参构造器 System.out.println(user); System.out.println("=============================="); //2.通过构造器构造对象 Constructor constructor = c1.getDeclaredConstructor(String.class); User user2 = (User)constructor.newInstance("你好");//括号里是参数 System.out.println(user2); System.out.println("=============================="); //3.通过反射调用普通方法 User user3 = (User) c1.newInstance(); //3.1获取一个方法 Method setName = c1.getDeclaredMethod("setName", String.class); //3.2invoke:激活的意思 //参数列表:(对象,"方法的值") setName.invoke(user3,"小明");//参数要包括user类的对象 //4.通过反射操作属性 System.out.println("=============================="); User user4 = (User) c1.newInstance(); Field name = c1.getField("name");//获取指定属性 //不能直接操作私有属性,可以选择关闭程序的安全检测,属性或方法的setAccessible(true)。若为true则属性和方法可以被随意调用,相反则不可以 name.setAccessible(true); //参数列表:(对象,"方法的值") name.set(user4,"小王"); System.out.println(user4.getName()); }} ParameterizedType:表示一种参数化类型,比如Collect GenericArrayType:表示一种元素类型是参数化类型或者类型变量的数组类型 TypeVariable:是各种类型变量的公共的接口 WildcardType:代表一种通配符类型的表达式 代码实现: import java.lang.reflect.*;import java.util.List;import java.util.Map;public class Test08 { public void test01(Map 运行结果如下: 代码实现: import java.lang.annotation.*;import java.lang.reflect.Field;//练习反射操作注解public class Test09 { public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException { Class c1 = Class.forName("注解和反射.反射.Student2"); //通过反射获取注解 Annotation[] annotations = c1.getAnnotations(); for (Annotation annotation : annotations) { System.out.println(annotation); } //获取TableJava注解中的value的值 //通过强制转换来获取注解对象 TableJava tablejava = (TableJava)c1.getAnnotation(TableJava.class); String value = tablejava.value(); System.out.println(value); //获取类的指定注解 //1,先获取类的属性 Field name = c1.getDeclaredField("name"); //2,再获取属性里面注解的对象 FieldJava annotation = name.getAnnotation(FieldJava.class); //3,通过获取的注解对象调用出其中的元素 System.out.println(annotation.columname()); System.out.println(annotation.length()); System.out.println(annotation.type()); }}@TableJava("学生")class Student2{ @FieldJava(columname = "卡号",type = "类型",length = 10) private int id; @FieldJava(columname = "年龄",type = "类型",length = 10) private String age; @FieldJava(columname = "姓名",type = "类型",length = 10) private String name; public Student2() { } public Student2(int id, String age, String name) { this.id = id; this.age = age; this.name = name; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getAge() { return age; } public void setAge(String age) { this.age = age; } public String getName() { return name; } 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 TableJava{ String value();}//属性的注解@Target(ElementType.FIELD)@Retention(RetentionPolicy.RUNTIME)@interface FieldJava{ String columname(); String type(); int length();}
Class c3 = Student.class;方式二:已知某个类的实例,调用该实例的getClass()方法获取Class对象
Class c1 = person.getClass();方式三:已知一个类的全类名。且该类在类的路径,可通过Class类的静态方法forName()获取,可以抛出ClassNotFoundException异常
Class c2 = Class.forName(“注解和反射.反射.Test02”);方式四:内置基本数据类型可以直接类名.Type
Class c4 = Integer.TYPE;