注解和反射
注解
1.什么是注解
1.注解(annotation)用来定义一个类、属性或一些方法,以便于程序能被编译处理。它相当于一个说明文件,告诉应用程序某个被注解的类或属性是什么,要怎么处理。注解可以用于标注包、类、方法和变量。
- Annotation是从JDK5.0开始引入的技术。
- Annotation的作用:
- 不是程序本身,可以对程序作出解释(这- - -点和注释(comment)没什么区别)
- 可以被其他程序(比如:编译器等)读取。
- Annotation的格式:
- 注解是以“@注释名”在代码中存在的还可以添加一-些参数值,例如:@xxx(value=“unchecked”)。
- Annotation在哪里使用?
- 可以附加在package (包), class(类) , method(方法) , field(字段)等_上面,相当于给他们添加了额外的辅助信息,我们可以通过反射机制编程实现对这些元数据的访问。
2.内置注解
注解 | 说明 |
---|---|
@Override | 用来修饰方法,表示此方法重写了父类方法 |
@Deprecated | 用于修饰方法,表示此方法已经过时。经常在版本升级后会遇到 |
@SuppressWarnnings | 告诉编译器忽视某类编译警告 |
@SuppressWarnnings注解选择使用属性
- unchecked:未检查的转换
- unused:未使用的变量
- resource:泛型,即未指定类型
- all:代表全部类型警告
- …
3.元注解
- 元注解的作用就是负责注解其他注解,java定义了4个标准的meta-annotation类型,他们被用来提供对其他annotation类型作说明
- 这些类型和它们所支持的类在java.lang.annotation包中可以找到(@Target,@Retention,@Documented,@Inherited)。
- @Target:用来描述注解的使用范围(被描述的注解可以用在什么地方)
- @Retention:表示需要在什么级别保存该注释信息,用于描述注解的生命周期。SOURCE(原代码的时候注解有用)< CLASS(编译后) < RUNTIME(运行时)
- @Document: 说明该注解将被包含在javadoc中。
- @Inherited: 说明子类可以继承父类中的该注解。
//Retention 表示我们的注解在有效范围。runtime>class> sources (范围:运行时 > 类 > 源码)
@Retention(value = RetentionPolicy.RUNTIME)
//Target 表示我们的注解可以用在哪些地方。ElementType.METHOD方法级别,ELementType.TYPE类级别
@Target(value = {ElementType.METHOD,ElementType.TYPE})
//Documented 表示是否将我们的注解生成在JAVAdoc中,(doc:文档)。
@Documented
//Inherited 表示子类可以继承父类的注解。
@Inherited
@interface MyAnnotation {
}
4.自定义注解
- 使用@interface自定义注解时,自动继承了java.lang.annotation.Annotation接口
- 分析:
- @ interface用来声明一个注解,格式: public @ interface注解名{定义内容}
- 其中的每一个方法实际上是声明了一个配置参数。
- 方法的名称就是参数的名称。
- 返回值类型就是参数的类型(返回值只能是基本类型,Class , String , enum )。
- 可以通过default来声明参数的默认值。
- 如果只有一个参数成员,一般参数名为value。
- 注解元素必须要有值,我们定义注解元素时,经常使用空字符串,0作为默认值。
@Retention(value = RetentionPolicy.RUNTIME)
@Target(value = {ElementType.METHOD,
@interface MyAnnotation {
String name() default "";
int age() default 0;
int id() default 1;
}
public class Test01 {
//注解显示赋值,如果没有默认值,就必须给注解赋值
@MyAnnotation(age = 20,name = "彭某")
public void test() {
}
}
反射
1.什么是反射
- Java反射机制的核心是在程序运行时动态加载类并获取类的详细信息,从而操作类或对象的属性和方法,本质是JVM得到class对象之后,再通过class对象进行反编译,从而获取对象的各种信息。
- Java属于先编译再运行的语言,程序中对象的类型在编译器就确定下来了,而程序在运行时可能需要动态加载某些类,这些类因为之前用不到,所以没有被加载到JVM。通过反射,可以在运行时动态的创建对象并调用属性,不需要提前在编译期知道运行的对象是谁。
静态语言VS动态语言
动态语言
➢是一类在运行时可以改变其结构的语言:例如新的函数、对象、甚至代码可以被引进,已有的函数可以被删除或是其他结构上的变化。通俗点说就是在运行时,代码可以根据某些条件改变自身结构。
➢主要动态语言: Object-C、C#、 JavaScript、 PHP、 Python等。
静态语言
➢与动态语言相对应的,运行时结构不可变的语言就是静态语言。如Java、 C、C++。
➢Java不是动态语言,但Java可以称之为“准动态语言”。即Java有一定的动态性,我们可以利用反射机制获得类似动态语言的特性。Java的动态性让编程的时候更加灵活!
2.反射的优缺点:
1、优点:在运行时获得类的各种内容,进行反编译,对于Java这种先编译再运行的语言,能够让我们很方便的创建灵活的代码,这些代码可以在运行时装配,无需在组件之间进行源代码的链接,更加容易实现面向对象。
2、缺点:(1)反射会消耗一定的系统资源,因此,如果不需要动态地创建一个对象,那么就不需要用反射;
(2)反射调用方法时可以忽略权限检查,因此可能会破坏封装性而导致安全问题。
3.反射的用途
- 反编译:.class—>.java
- 通过反射机制访问java对象的属性,方法,构造方法等
- 当我们在使用开发工具,比如IDEA时,当我们输入一个对象或者类,并想调用他的属性和方法是,一按点号,编译器就会自动列出他的属性或者方法,这里就是用到反射。
- 反射最重要的用途就是开发各种通用框架。spring…
4.反射相关的主要API
- java.lang.Class:代表一个类
- java.lang.reflect.Method:代表类的方法
- java.lang.reflect.Field:代表类的成员变量
- java.lang.reflect.Constructo:代表类的构造器
- java.lang.Array:提供了动态创建数组,以及访问数组的元素的静态方法
- …
5.Class类
对象照镜子后可以得到的信息:某个类的属性、方法和构造器、某个类到底实现了哪些接口。对于每个类而言, JRE都为其保留一个不变的Class类型的对象。
- Class本身也是一个类
- Class对象只能由系统构建
- 一个加载的类在JVM中只会有一个Class实例
- 每个类的实例都会记得自己是由哪个Class实例所生成
- 通过Class可以完整地得到一个类中所有被加载的结构
在Object类中定义了以下的方法,此方法将被所有子类继承。
public final Class getClass()
以上的方法返回值的类型是一 个Class类,此类是Java反射的源头,实际上所谓反射从程序的运行结果来看也很好理解,即:可以通过对象反射求出类的名称。
5.1获取Class类的实例
通过类的class属性获取
//知道具体的类,通过类的class属性获取,该方法最为安全可靠,程序性能最高
Class c1 = Person.class;
调用该对象的getClass()方法获取
//知道某个类的实例(对象),调用该对象的getClass()方法获取Class对象
Person person = new Person();
Class c1 = person.getClass();
通过Class类的静态方法forName()获取
//知道类的全路径和类名,可以通过Class类的静态方法forName()获取
Class c1=Class.forName("Scanner.Person");
5.2哪些类型可以有Class对象?
- class:外部类,内部类(成员内部类,静态内部类,局部内部类,匿名内部类)
- interface:接口
- []:数组
- enum:枚举
- annotation:注解@interface
- primitive type:基本数据类型
- void
6.Java内存分析
java内存有3块
栈(存放基本变量类型:(会包含基本数据类型的具体数值),引用对象的变量:(会存放引用在堆里面的具体地址))
堆(存放new的对象和数组,可以被所有的线程共享,不会存放别的对象引用)
方法区(可以被所有的线程共享,包含了所有的class和static变量)
7.获取类运行时的结构
public class Test {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException {
//获取Class的信息
Class c1 = Class.forName("Scanner.Person");
//获取类的名字
System.out.println(c1.getName()); //获得包名+类名
System.out.println(c1.getSimpleName()); //获得类名
//获得类的属性
Field[] fields = c1.getFields();//只能找到public属性
for (Field field : fields) {
System.out.println(field);
}
System.out.println("+++++++++++++++++++++");
fields = c1.getDeclaredFields(); //找到全部类型的属性
for (Field field : fields) {
System.out.println(field);
}
//获取指定的属性
Field name = c1.getDeclaredField("name");
System.out.println(name);
//获得类的方法
Method[] methods = c1.getMethods();
for (Method method : methods) {
System.out.println("获得本类的公共方法" + method);
}
methods = c1.getDeclaredMethods(); //获取本类的所有方法
for (Method method : methods) {
System.out.println("获得本类所有的方法" + method);
}
//获得指定的方法没有参数写null
Method getName = c1.getMethod("getName", null);
Method setName = c1.getMethod("setName", String.class);
System.out.println(getName);
System.out.println(setName);
//获得指定的构造器
Constructor[] constructors = c1.getConstructors(); //获取public构造方法
for (Constructor constructor : constructors) {
System.out.println(constructor);
}
//获得全部方法包括private方法
Constructor[] constructors = c1.getDeclaredConstructors();
for (Constructor constructor : constructors) {
System.out.println("#"+constructor);
}
//获得指定的构造方法
Constructor declaredConstructor = c1.getDeclaredConstructor(String.class,int.class,char.class);
System.out.println(declaredConstructor);
}
}
如何取得属性、方法、构造器的名称,修饰符等
public class test02 {
public static void main(String[] args) throws Exception{
//获得Class对象
Class c1 = Class.forName("Scanner.Person");
//构造一个对象
Person person = (Person) c1.newInstance();//本质调用了无参构造器,需要有无参构造
System.out.println(person);
//用构造方法创建对象(也可以使用无参构造)
Constructor constructor = c1.getDeclaredConstructor(String.class, int.class, char.class);
Person person1 = (Person) constructor.newInstance("彭某", 18, '男');
System.out.println(person1.toString());
//用反射调用普通方法
Person person2 = (Person) c1.newInstance();
person2.setName("彭某");
System.out.println(person2.getName());
//通过反射获取一个方法
Method setName = c1.getDeclaredMethod("setName", String.class);
setName.invoke(person2, "吴某");
System.out.println(person2.getName());
//通过反射操作属性(操作属性不能直接操作私有属性,我们需要关闭程序的安全检测)
Person person4 = (Person) c1.newInstance();
Field name = c1.getDeclaredField("name");
name.setAccessible(true);//关闭安全检测
name.set(person,"倪某");
System.out.println(person4.getName());
}
}
setAccessible方法
- Method(方法)和Field(属性)、Constructor(构造器)对象都有setAccessible()方法。
- setAccessible作用是 启动和禁用访问安全检查的开关。
- 参数值为true则指示反射的对象在使用时应该取消Java语言访问检查。
- 提高反射的效率。如果代码中必须用反射,而该句代码需要频繁的被调用,那么请设置为true。
- 使得原本无法访问的私有成员也可以访问。
- 参数值为false则指示反射的对象应该实施Java语言访问检查。
性能测试
public class Test03 {
public static void test01() {
Person person = new Person();
long startTime = System.currentTimeMillis();
for (int i = 0; i < 1000000000; i++) {
person.getName();
}
long endTime = System.currentTimeMillis();
System.out.println("普通方法执行"+(endTime-startTime)+"ms");
}
//反射方式调用
public static void test02() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Person person = new Person();
Class c1 = person.getClass();
//获得方法
Method getName = c1.getDeclaredMethod("getName",null);
long startTime = System.currentTimeMillis();
for (int i = 0; i < 1000000000; i++) {
//操作方法
getName.invoke(person,null);
}
long endTime = System.currentTimeMillis();
System.out.println("反射方式执行"+(endTime-startTime)+"ms");
}
//反射方式调用 关闭检测
public static void test03() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Person person = new Person();
Class c1 = person.getClass();
Method getName = c1.getDeclaredMethod("getName",null);
getName.setAccessible(true);
long startTime = System.currentTimeMillis();
for (int i = 0; i < 1000000000; i++) {
getName.invoke(person,null);
}
long endTime = System.currentTimeMillis();
System.out.println("反射方式关闭检测执行"+(endTime-startTime)+"ms");
}
public static void main(String[] args) throws Exception{
test01();
test02();
test03();
}
}
8.通过反射操作注解
getAnnottations
getAnnottation
//属性的注解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface Field {
String name();
String type();
int length();
}
//类名的注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface Table {
String value();
}
public class Test04 {
public static void main(String[] args) throws Exception {
//获得Class对象
Class c1 = Class.forName("Scanner.Student");
//通过反射获得注解
Annotation[] annotations = c1.getAnnotations();//获得注解
for (Annotation annotation : annotations) {
System.out.println(annotation);//输出注解
}
//获得注解的value值
Table table = (Table) c1.getAnnotation(Table.class);
String value = table.value();//获得注解的value
System.out.println(value); //输出注解
//获得指定类的注解
java.lang.reflect.Field id = c1.getDeclaredField("id");//获得字段注解的指定字段注解信息
Field annotation = id.getAnnotation(Field.class);
System.out.println(annotation.name());
System.out.println(annotation.type());
System.out.println(annotation.length());
}
}
@Table("student")
class Student {
@Field(name = "id", type = "int", length = 10)
private int id;
@Field(name = "name", type = "varchar", length = 255)
private String name;
@Field(name = "age", type = "int", length = 2)
private int 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;
}
}