注解与反射都是框架的基础
注解
-
注解的格式:@注释名(参数名=参数值)
-
可以使用在:package、class、method、field上,作为辅助信息
内置注解
- @Override:重写方法,会检测方法名称
- @Deprecated:表明该方法已过时,通常会存在更好的替代方法,调用时会有删除线:
test() - @SupressWarnings:镇压警告,可以放在类上也可以在方法上等等
元注解 meta-notation
负责注解其他注解,有四种元注解:
- @Target:使用范围 ,@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
- @Retention:生命周期,SOURCE源码注解生效<CLASS生成类时有效<RUNTIME运行时有效(常用)
- @Document:生成文档
- @Inherited:子类继承注解
//测试元注解
public class Test02 {
@MyAnnotation("param")
public void test() {
}
}
//定义一个注解
@Target(value= {ElementType.METHOD,ElementType.TYPE})//用在方法、类上
@Retention(value=RetentionPolicy.RUNTIME) //运行时也有效
@Documented //是否声称在JavaDoc文档中
@Inherited //子类可以继承父类的注解
@interface MyAnnotation{
String[] value(); //设定参数
}
自定义注解
-
@Interface:自定义注解
-
参数格式:参数类型+参数名();
//自定义注解
public class Test03 {
@MyAnnotation2(name="周逸",schools= {"南京邮电大学"}) //只有一个可以省略value
public void test() {}
}
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation2{
//注解参数编写格式: 参数类型+参数名();
String name() default ""; //默认为空
int age() default 0;
int id() default -1; //如果找不到,返回-1
String[] schools() default {"清华大学"};
}
反射
静态Vs动态语言
动态语言:运行时可以改变结构的语言,Python、Javascript、C#
静态语言:结构不可变的语言,Java(反射机制——准动态语言)、C、C++
Reflection
借助反射API获取任何类的内部信息,并操作任何属性和方法。
反射是什么呢?当我们的程序在运行时,需要动态的加载一些类这些类可能之前用不到所以不用加载到jvm,而是在运行时根据需要才加载》
获取类的几种方式
注意:一个加载的类在JVM种只有一个Class实例
//通过反射获取Class对象
public class Test01 {
public static void main(String[] args) throws ClassNotFoundException {
//forname
Class c1=Class.forName("com.bigdata.reflection.User");
System.out.println(c1.hashCode()); //User
//对象
User user = new Student();
Class c2=user.getClass();
System.out.println(c2.hashCode()); //Student
//类
Class c3=Student.class;
System.out.println(c3.hashCode());// 一个对象在JVM中只有一个Class
//基本类型的包装类
Class c4=Integer.TYPE;
System.out.println(c4); //int
//父类
Class c5=c2.getSuperclass();
System.out.println(c5); //com.bigdata.reflection.User
}
}
类加载内存分析(加载、链接、初始化)
-
在类加载时,Class对象就已经生成。
-
主动引用/反射,都会产生类的初始化,如果父类的静态资源没有被初始化,则先初始化父类的静态资源。
类加载器
作用:将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时数据结构,然后在堆中生成一个代表这个类的java.lang.Class对象,作为方法区中类数据的访问入口。
- 引导类加载器(bootstrap class loader):它用来加载 Java 的核心库,是用C++代码来实现的,并不继承自
java.lang.ClassLoader
。rt.jar - 扩展类加载器(extensions class loader):它用来加载 Java 的扩展库。Java 虚拟机的实现会提供一个扩展库目录。该类加载器在此目录里面查找并加载 Java 类。jre/lib/ext下的jar
- 系统类加载器(system class loader):它根据 Java 应用的类路径(CLASSPATH)来加载 Java 类。一般来说,Java 应用的类都是由它来完成加载的。可以通过
ClassLoader.getSystemClassLoader()
来获取它。java-classpath
双亲委派:保证安全性,多重检测,如果在rt包中已经存在java.lang.String,手写的String包则失效。
获取类的运行时结构
运行时可以通过反射机制,获取类的代码信息:属性、方法、构造器
public class Test04 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, SecurityException, NoSuchMethodException {
Class clz=Class.forName("com.bigdata.reflection.User");
System.out.println(clz.getName());
System.out.println(clz.getSimpleName());
//获得属性
Field[] fields=clz.getDeclaredFields();
for(Field field:fields) {
System.out.println(field);
}
Field field=clz.getDeclaredField("name"); //declared可查询private属性
System.out.println(field);
//获得方法
Method[] methods=clz.getDeclaredMethods(); //获得本类的所有方法
for(Method method:methods) {
System.out.println("declared:"+method);
}
methods=clz.getMethods(); //本类及父类的public方法
for(Method method:methods) {
System.out.println("method:"+method);
}
Method getName=clz.getMethod("getName", null);
Method setName=clz.getMethod("setName", String.class); //由于java有重载,需要参数类型来确定方法
System.out.println(getName);
System.out.println(setName);
//获得指定构造器
Constructor[] constructors=clz.getConstructors();
for(Constructor constructor:constructors)
System.out.println(constructor);
Constructor constructor=clz.getConstructor(String.class,int.class);
System.out.println(constructor);
}
}
获取了这些属性有什么作用?通过反射来创建一个对象
//构建一个对象
User user=(User)clz.newInstance(); //本质:调用类的无参构造器
System.out.println(user);
//通过构造器来创建对象
Constructor constructor=clz.getConstructor(String.class,int.class);
User user2=(User) constructor.newInstance("周逸",18);
System.out.println(user2);
//调用方法
User user3=(User)clz.newInstance();
Method setName=clz.getMethod("setName", String.class);
setName.invoke(user3, "carmine");
Method getName=clz.getMethod("getName", null);
System.out.println(getName.invoke(user3, null));
//操作属性
User user4=(User)clz.newInstance();
Field name=clz.getDeclaredField("name");
name.setAccessible(true);//关闭权限检测来访问私有属性(不安全!)
name.set(user4, "carmineYi");
System.out.println(user4.getName());
setAccessible关闭检测
反射方式效率较低,关闭检测可以提升性能效率
反射操作泛型
Type[] genericParameterTypes=method.getGenericParameterTypes() //获取泛型参数
Type[] genericReturnTypes=method.getGenericReturnType() //获取泛型返回值
总结
综合反射和注解打代码,也是Spring框架的底层原理,利用反射操作注解:
public class Test05 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, SecurityException {
Class clz=Class.forName("com.bigdata.reflection.Student2");
//获得注解
Annotation[] annotations=clz.getAnnotations();
for(Annotation annotation:annotations)
System.out.println(annotation);
//获得注解的value值
TableCamine tablecarmine=(TableCamine) clz.getAnnotation(TableCamine.class);
String value=tablecarmine.value();
System.out.println(value);
//获得name属性的指定注解
Field field=clz.getDeclaredField("name");
FieldCamine fieldcarmine=field.getAnnotation(FieldCamine.class);
System.out.println(fieldcarmine.columnName());
System.out.println(fieldcarmine.type());
System.out.println(fieldcarmine.length());
}
}
//类名注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface TableCamine{
String value();
}
//模拟数据库属性
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface FieldCamine{
String columnName(); //列名
String type(); //类型
int length(); //长度
}
//模拟数据库建表
@TableCamine("db_student")
class Student2
{
@FieldCamine(columnName="db_id",type="int",length=10)
private int id;
@FieldCamine(columnName="db_age",type="int",length=10)
private int age;
@FieldCamine(columnName="db_name",type="varchar ",length=3)
private String name;
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;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Student [id=" + id + ", age=" + age + ", name=" + name + "]";
}
}