注解和反射
注解入门
1.Annotation 作用:
- 不是程序本身,可以对程序做出解释;
- 可以被其他程序如编译器读取;
2.格式及使用
- 格式:@注释名
- 使用方式:在package,class,method,field上面,相当于添加了额外的辅助信息,可以通过反射机制实现对这些元数据的访问。
3.常见注解
- @Override 重写超类的方法
- @Deprecated: 不推荐程序使用,但是可以使用,有更好的实现方式
- @SuppressWarning 压制警告信息
4.元注解
- 作用:负责注解其他注解的使用范围,描述注解的生命周期
- 分类:四个标准的元注解
@Target :描述注解的使用范围,注意可以放置多个范围{ElementType.TYPE,Element.FILED}
范围关键字 | 使用方式 | 解析 |
---|---|---|
TYPE | @Target(ElementType.TYPE) | 用在类、接口或枚举类中 |
FIELD | @Target(ElementType.FILED | 用在属性上 |
METHOD | @Target(ElementType.METHOD | 用在方法上 |
PACKAGE | @Target(ElementType.PACKAGE) | 用在包上 |
… | … |
@Retention:表示需要在什么级别保存该注释信息,用于描述注解的生命周期
SOURCE<CLASS<RUNTIME
生命周期关键字 | 使用方式 | 解析 |
---|---|---|
RUNTIME | @Retention(RetentionPolicy.RUNTIME) | 在运行时有效,在运行时节点可以通过反射获取 |
SOURCE | @Retention(RetentionPolicy.SOURCE) | 在源代码时候可以 |
CLASS | @Retention(RetentionPolicy.CLASS) | 在类上是可用 |
@Document:说明注解将被包含在javadoc中
@Inherited:说明子类可以继承父类中的该注解
//测试元注解
public class Test02 {
@MyAnnotation
public void test(){
}
}
//定义一个注解
//Target 表示注解可以用在哪些地方,方法上,类上,可以放多个
@Target(value = {ElementType.METHOD,ElementType.TYPE})
//Retention 表示的注解在什么地方有效
//runtime>class>sources
@Retention(value= RetentionPolicy.RUNTIME)
//Document 表示是否将将注解生成在javadoc中
@Documented
//Inherited 子类可以继承父类的注解
@Inherited
@interface MyAnnotation{
}
5.自定义注解
格式:
@Traget
@interface MyAnnotion{
//注解参数格式:
//字段类型,参数值
String value();
}
public class Test03 {
//注解可以显示,如果没有默认值,必须使用注解赋值
@MyAnnotation2(name = "zhaocai",age = 2)
public void test(){
}
@MyAnnotation3("")
public void test3(){
}
}
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation2{
//注解的参数:参数类型+参数名();
String name() default "";
int age() default 0;
int id() default -1;//如果默认值为-1,代表不存在
String[] schools() default {"td","qh"};
}
@Target({ElementType.FIELD,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation3{
String value(); //一个参数的时候参数名使用value
}
反射机制
1.动态语言&静态语言
- 动态语言:在运行时可以改变其结构的语言,包括JavaScript 等
- 静态语言:运行时结构不可变的语言;
java不是动态语言,但是通过反射机制,可以成为准动态语言,使得编程时候更加灵活,但同时会存在效率降低,安全性降低的问题;
Reflection,允许程序在执行期间通过Reflection API取得任何类的内部信息,加载完类之后,在堆内存的方法区产生了一个Class对象,这个对象包含了完整的类的结构信息,并能任意操作任意对象的内部属性及方法;
2.反射过程
实例化对象->getClass()方法->得到完整的“包类”名称
3.反射机制提供的功能
- 在运行时判断任意一个对象所属的类
- 在运行时构造任意一个类的对象
- 在运行时判断任意一个类所具有的成员变量和方法
- 在运行时获取泛型信息
- 在运行时调用任意一个对象的成员变量和方法
- 在运行时处理注解
- 生成动态代理
4.java反射优缺点
- 优点:实现动态创建对象和编译,比较灵活
- 缺点:性能较差
5.相关API
- java.lang.Class 代表一个类
- java.lang.reflet.Method 代表类的方法
- java.lang.reflect.Field 代表类的成员信息
- java.lang.reflect.Constructor 代表类的构造器
//反射应用
public class Relection01 {
public static void main(String[] args) throws ClassNotFoundException {
//通过反射过去类的class对象
Class c1 = Class.forName("com.jf.reflection.User");
System.out.println(c1);
Class c2 = Class.forName("com.jf.reflection.User");
Class c3 = Class.forName("com.jf.reflection.User");
//一个类在内存中只有一个Class对象
//一个类被加载后,类的整个结构都会封控在Class对象中
//通过比较类的hashcode可以判断是否为同一个对象
System.out.println(c1.hashCode());
System.out.println(c2.hashCode());
System.out.println(c3.hashCode());
}
}
//实体类 一般放在pojo或entity包下
class User{
private int id;
private int age;
public User(int id, int age) {
this.id = id;
this.age = age;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
1.获取Class类的方式
获取Class | 解析 |
---|---|
Class.forName(“具体包名”) | 通过包名获取类 |
对象.getClass() | 通过对象获取类 |
类名.class | 通过类名获取类 |
子类对象.getSuperClass() | 通过子类对象获取父类 |
//测试Class类的创建方式
public class Reflection02 {
public static void main(String[] args) throws ClassNotFoundException {
Person person = new Student();
System.out.println("这个人是:"+person.name);
//方式一:有对象,通过 对象.getClass()方式创建
Class c1=person.getClass();
System.out.println(c1.hashCode());
//方式二:有包名,用forName获取对象
Class c2 = Class.forName("com.jf.reflection.Student");
System.out.println(c2.hashCode());
//方式三:通过类名.class的方式获得
Class c3 = Student.class;
System.out.println(c3.hashCode());
//方式四:基本内置类型的包装类,都有一个Type属性
Class c4 = Integer.TYPE;
System.out.println(c4.hashCode());
//通过子类对象获得父类类型
Class c5 = c1.getSuperclass();
System.out.println(c5);
}
}
class Person{
public String name;
public Person() {
}
public Person(String name) {
this.name = name;
}
@Override
public String toString() {
return "name="+name;
}
}
class Student extends Person{
public Student(){
this.name = "学生";
}
}
class Teacher extends Person{
public Teacher(){
this.name = "老师";
}
}
2.哪些类型有class对象
- calss 外部类,成员,局部内部类,匿名内部类
- interface 接口
- [] 数组
- enum 枚 举
- annotation:注解 @interface
- primitive type: 基本数据类型
- void
public class Reflection03 {
public static void main(String[] args) {
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
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);
System.out.println(c8);
System.out.println(c9);
//只要元素类型与维度一样,就是同一个class
int[] a = new int[10];
int[] b = new int[100];
Class a1 = a.getClass();
Class b1 = b.getClass();
System.out.println(a1.hashCode());
System.out.println(b1.hashCode());
}
}
3.java内存分析
1.内存分析:
- 堆:存放new出来的对象和数组 可以被所有的线程共享,不会存放别的对象引用
- 栈:存放基本变量类型,包含具体数值;存放引用类型变量,引用对堆里对应对象的地址
- 方法区:可以被所有线程共享,包含了所有的class和static变量
2.类的初始化
使用某个类,并且该类未被加载到内存中,则会进行一下三个步骤进行初始化
- 类的加载:将类的class文件读入内存,并为之创建一个java.lang,Class对象
- 类的链接:将类的二进制数据合并到jre中,
- 类的初始化:JVM负责对类进行初始化
3.类加载过程实例:
- 类加载,java程序编译成.class文件,加载到内存中,放到方法区保留Test05类数据,A类数据,加载完成对应会在堆中生成一个java.lang,Class对象分别代代表Test05类和A类;
- 链接,Test05类中,Main()方法,相关信息会存在栈中,静态变量m存在栈中,并且设置初始化值 m=0;
- 初始化,在堆中new A(),创建了一个A类的对象,指向A类的class,.m先赋值300,最后一次赋值为100;最终输出为100;
public class Reflection04 {
public static void main(String[] args) {
A a = new A();
System.out.println(A.m);
/**
* 1.加载到内存,会产生一个类对应Class对象
* 2.链接,链接结束后 m=0
* 3.初始化
* <clinit>(){
* System.out.println("A类静态代码块初始化");
* m = 300;
* m = 100;
* }
* m= 100
* */
}
}
class A{
static {
System.out.println("A类静态代码块初始化");
m = 300;
}
/*
*m = 300;
* m = 100; //重新赋值100
* */
static int m = 100;
public A(){
System.out.println("A类无参构造方法");
}
}
java核心包
rt.jar包
4.获取类加载器
public class Reflection05 {
public static void main(String[] args) throws ClassNotFoundException {
//获取系统类的加载器
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
System.out.println(systemClassLoader);
//获取系统类加载器的父类加载器->扩展类加载器
ClassLoader parent = systemClassLoader.getParent();
System.out.println(parent);
//获取扩展类加载器的父类加载器-->根加载器(c/c++)
ClassLoader p1 = parent.getParent();
System.out.println(p1);
//测试当前类是哪个加载器加载的
ClassLoader classLoader = Class.forName("com.jf.reflection.Reflection05").getClassLoader();
System.out.println(classLoader);
//测试jdk内置的类是谁加载的
classLoader = Class.forName("java.lang.Object").getClassLoader();
System.out.println(classLoader);
//如何获得系统类加载器可以加载的路径
System.out.println(System.getProperty("java.class.path"));
//双亲委派机制
//java.lang.String-->
}
}
5.获取运行时类的完整结构
public class Reflection06 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException {
Class c1 = Class.forName("com.jf.reflection.User");
//获得类的名字
System.out.println(c1.getName()); //获得包名+类名
System.out.println(c1.getSimpleName()); //获得类名
//获得类的属性
System.out.println("==============");
Field[] fields = c1.getFields();//只能找到public的属性
fields = c1.getDeclaredFields();//找到全部属性
for (Field field:fields){
System.out.println(field);
}
System.out.println("==============");
//获取指定属性的值
Field id = c1.getDeclaredField("id");
System.out.println(id);
//获得类的方法
System.out.println("============");
Method[] methods = c1.getMethods();//获得本类及其父类的全部public方法
for (Method method:methods){
System.out.println(method);
}
System.out.println("============");
methods = c1.getDeclaredMethods();//获得本类的所有方法,包括private
for (Method method:methods){
System.out.println(method);
}
//获得指定方法
System.out.println("==============");
Method getId = c1.getMethod("getId",null);
Method setId = c1.getMethod("setId", int.class);
System.out.println(getId);
System.out.println(setId);
System.out.println("==============");
Constructor[] constructors = c1.getConstructors();//获得声明的构造器
for (Constructor constructor:constructors){
System.out.println(constructor);
}
System.out.println("=============");
constructors = c1.getDeclaredConstructors();//获取所有的构造器
for (Constructor constructor:constructors){
System.out.println(constructor);
}
System.out.println("==============");
//获得指定的构造器,
Constructor declaredConstructor = c1.getDeclaredConstructor(int.class,int.class);
System.out.println(declaredConstructor);
}
}
6.获取Class
可创建对象,获取属性,方法,构造器
public class Reflection07 {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {
//获得Class
Class c1= Class.forName("com.jf.reflection.User");
//构造一个对象,无参构造
User user = (User) c1.newInstance();
System.out.println(user);
//通过先获取指定构造器,再创建对象
Constructor declaredConstructor = c1.getDeclaredConstructor(int.class,int.class);
User user1 = (User) declaredConstructor.newInstance(1,2);
System.out.println(user1);
//通过反射调用普通方法
User user2 = (User) c1.newInstance();
//通过反射获取一个方法
Method setId = c1.getDeclaredMethod("setId", int.class);
//invoke :激活的意思
//(对象,"方法的值")
setId.invoke(user2,222); //通过获取的方法给指定对象传参
System.out.println(user2.getId());
//通过反射操作属性
User user3 = (User) c1.newInstance();
Field id = c1.getDeclaredField("id");
//不能直接操作私有属性,需要关闭程序的安全监测,属性或方法的setAccessible(true)
id.setAccessible(true);
id.set(user3,333);
System.out.println(user3.getId());
}
}
7.反射操作泛型
先通过Class获取方法,然后再获取泛型参数;
//通过反射获取泛型
public class Reflection09 {
public void test01(Map<String,User> map,List<User> list){
System.out.println("test01");
}
public Map<String,User> test02(){
System.out.println("test02");
return null;
}
public static void main(String[] args) throws NoSuchMethodException {
Method method = Reflection09.class.getMethod("test01",Map.class, List.class);
//获取传入参数,及形参参数类型
Type[] genericParameterTypes = method.getGenericParameterTypes();
for (Type genericParameterType : genericParameterTypes) {
System.out.println("*"+genericParameterType);
if (genericParameterType instanceof ParameterizedType){
Type[] actualTypeArguments = ((ParameterizedType) genericParameterType).getActualTypeArguments();
for (Type actualTypeArgument : actualTypeArguments) {
System.out.println(actualTypeArgument);
}
}
}
method = Reflection09.class.getMethod("test02",null);
//获取返回参数
Type genericReturnType = method.getGenericReturnType();
if (genericReturnType instanceof ParameterizedType){
Type[] actualTypeArguments = ((ParameterizedType) genericReturnType).getActualTypeArguments();
for (Type actualTypeArgument : actualTypeArguments) {
System.out.println(actualTypeArgument);
}
}
}
}
//练习反射操作注解
public class Reflection10 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
Class c1 = Class.forName("com.jf.reflection.Student2");
//通过反射获得注解
Annotation[] annotations = c1.getAnnotations();
for (Annotation annotation : annotations) {
System.out.println(annotation);
}
//获得注解value的值
TableStudent tableStudent = (TableStudent) c1.getAnnotation(TableStudent.class);
String value = tableStudent.value();
System.out.println(value);
//获得属性指定的注解
Field f = c1.getDeclaredField("name");
FieldStudent fieldStudent = (FieldStudent) f.getAnnotation(FieldStudent.class);
System.out.println(fieldStudent.columnName());
System.out.println(fieldStudent.type());
System.out.println(fieldStudent.length());
}
}
@TableStudent("db_student")
class Student2{
@FieldStudent(columnName = "db_id",type = "int",length = 10)
private int id;
@FieldStudent(columnName = "db_age",type = "int",length = 10)
private String name;
@FieldStudent(columnName = "db_name",type = "varchar",length = 3)
private int age;
public Student2() {
}
public Student2(int id, String name, int age) {
this.id = id;
this.name = name;
this.age = age;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student2{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
'}';
}
}
//类名注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface TableStudent{
String value();
}