注解(Annotation)
注解是从JDK5.0开始引入的技术
注解的作用
注解相当与于是程序的检查和约束
不是程序本身,只是可以对程序作出解释(与注释(comment)没什么区别)
可以被其他程序(比如:编译器等)读取——》通过反射读取
注解的格式
注解是以“@注释名”在代码中存在的,还可以添加一些参数值,
例如:@Suppress Warning(value=“uncheckked”)
package com.wu.annotation;
import java.util.ArrayList;
import java.util.List;
//什么是注解
@SuppressWarnings("all")//镇压全部警告
public class Test01 extends Object {
//@Override 重写的注解
@Override
public String toString() {
return super.toString();
}
//@Deprecated 不推荐使用,但是可以使用,或者说存在更好的方式
@Deprecated
public static void test(){
System.out.println("Deprecated");
}
public void test02(){
List list = new ArrayList();
}
public static void main(String[] args) {
test();
}
}
注解在哪里使用
可以添加在package,class,method,filed等上面,相当于给他们添加了额外的辅助信息,
我们可以通过反射机制编程实现对这些元数据的访问
作用:负责注解其他注解的注解
Java定义了四个元注解(meta-annotation)分别是@Target,@Retention,@Documented,@Inherited
这四个元注解存在java.lang.annotation包中
@Target(重点):用于描述注解的使用范围(即被描述的驻俄界可以用在什么地方)
@Retention(重点):表示需要在什么级别保存该注释信息,用于描述注解的生命周期(SOURCE
@Documented:说明该注解将被包含在javadoc中
@Inherited:说明子类可以继承父类的该注解
package com.wu.annotation;
import java.lang.annotation.*;
//测试元注解
@MyAnnotation
public class Test02 {
public void test(){
}
}
//定义一个注解
//@Target 表示我们的注解可以用在哪些地方。
@Target(value = {ElementType.METHOD,ElementType.TYPE})
//@Retention表示我们的注解在什么地方还有效
//runtime>class>sources
@Retention(value = RetentionPolicy.RUNTIME)
//@Documented 表示是否将我们的注解生成在JAVAdoc中
@Documented
//@Inherited 子类可以继承父类的注解
@Inherited
//@interface自定义注解
@interface MyAnnotation{
}
@interface自定义注解 =》格式:@interface注解名{定义内容}
package com.wu.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
public class Test03 {
//注解可以显示赋值,如果没有默认值,就必须给注解赋值
@MyAnnotation2(name = "wu",age = 18)
public void test(){}
@MyAnnotation3( "wu" )
public void test2(){}
}
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation2{
//注解的参数:参数类型+参数名();
String name()default "";//default 注解默认值
int age () default 0;
int id () default -1;//如果默认值为-1,代表不存在
String[] schools()default {"河南","黄淮学院"};
}
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation3{
String value();
}
主要的动态语言:Object-C、C#、JavaScript、PHP、Python等。
Relection(反射)时Java被视为动态语言的关键,反射机制允许程序在执行期间借助于ReflectionAPI取得任何类的内部信息,并能直接操作任意对象的内部属性及方法
Class c = Class.forName("java.lang.String")
加载完类之后,在堆内存的方法区中就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象就包含了完整的类的结构信息。我们可以通过这个类看到对象的结构。这个对象就像一面镜子,透过这个镜子看到类的结构,所以,我们形象的称之为:反射
对性能有影响。使用反射机制基本上是一种解释操作,我们可以告诉JVM,我们希望做什么并且它满足我们的要求。这类操作总是慢于直接执行相同的操作。
java.lang.Class:代表一个类
java.lang.reflect.Field:代表类的成员变量
java.lang.reflect.Method:代表类的方法
java.lang.ref;ect.Constructor:代表类的构造器
·················
在Object类中定义了以下的方法,此方法将被所有子类继承
public final Class getClass()
以上方法返回值的类型时一个Class类,此类是Java反射的源头。即可以通过对象反射求出类的名称。
Class本身也是一个类
Class对象只能由系统建立对象
一个加载的类在JVM中只会有一个Class实例
一个Class对象对应的是一个加载到JVM中的一个.class文件
每个类的实例都hi记得自己是由哪一个Class实例所生成的
通过Class可以完整的得到一个类中所有被加载的结构
Class类是Reflection的根源,针对任何你想动态加载,运行的类,唯有先获得相应的Class对象
package com.wu.reflection;
//测试Class类的创建方式有哪些
public class Test03 {
public static void main(String[] args) throws ClassNotFoundException {
Person person = new Student();
System.out.println("这个人是:"+person.name);
//方式一:通过对象获得
Class c1= person.getClass();
System.out.println(c1.hashCode());//1956725890
//方式二:forName获得
Class c2 = Class.forName("com.wu.reflection.Student");
System.out.println(c2.hashCode());//1956725890
//方式三:通过类名.Class获得
Class<Student> c3 = Student.class;
System.out.println(c3.hashCode());//1956725890
//方式四:基本内置类型的包装类都有一个Type属性
Class c4 = Integer.TYPE;
System.out.println(c4);//int
//获得父类类型
Class c5 = c1.getSuperclass();
System.out.println(c5);//class com.wu.reflection.Person
}
}
class Person{
public String name;
public Person() {
}
public Person(String name) {
this.name = name;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
'}';
}
}
class Student extends Person{
public Student(){
this.name="学生";
}
}
class Teacher extends Person{
public Teacher(){
this.name="老师";
}
}
class:外部类,成员(成员内部类,静态内部类),局部内部类,匿名内部类。
interface:接口
[]:数组
enum:枚举
annotation:注解@interface
primitive type:基本数据类型
void
package com.wu.reflection;
import java.lang.annotation.ElementType;
//所有类型的Class对象
public class Test04 {
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];
System.out.println(a.getClass().hashCode());//1956725890
System.out.println(b.getClass().hashCode());//1956725890
}
}
当程序使用某个类时,如果该类还未被加载到内存中,则系统会通过下面三个步骤来对该类进行初始化
类的主动引用(一定会发生类的初始化)
类的被动引用(不会发生类的初始化)
类加载器的作用:将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时数据结构,然后在堆中生成一个代表这个类的java.lang.Class对象,作为方法区中类数据的访问入口。
类缓存:标准的JavaSE类加载器可以按要求查找类,但一旦某个类被加载到类加载器中,它将维持加载(缓存)一段时间。不过JVM垃圾回收机制可以回收这些Class对象。
源程序(*.java文件)=》Java编译器 =》字节码( *.class文件)=》类装载器 =》字节码校验器 =》解释器 = 》操作系统平台
类加载器的作用就是用来把类(class)装在进内存。
类加载器的类型
package com.wu.reflection;
public class Test07 {
public static void main(String[] args) throws ClassNotFoundException {
//获取系统类的加载器
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@74a14482
//获取扩展类加载器的父类加载器--》根加载器(C/C++)
ClassLoader parent1 = parent.getParent();
System.out.println(parent1);//null 无法直接获取
//测试当前类是哪个加载器加载的
ClassLoader classLoader = Class.forName("com.wu.reflection.Test07").getClassLoader();
System.out.println(classLoader);//sun.misc.Launcher$AppClassLoader@18b4aac2
//测试JDK内置类是哪个加载器加载的
classLoader = Class.forName("java.lang.Object").getClassLoader();
System.out.println(classLoader);//null
//如何获得系统类加载器可以加载的路径
System.out.println(System.getProperty("java.class.path"));
//双亲委派机制
/*
K:\jdk\jre\lib\charsets.jar;
K:\jdk\jre\lib\deploy.jar;
K:\jdk\jre\lib\ext\access-bridge64.jar;
K:\jdk\jre\lib\ext\cldrdata.jar;
K:\jdk\jre\lib\ext\dnsns.jar;
K:\jdk\jre\lib\ext\jaccess.jar;
K:\jdk\jre\lib\ext\jfxrt.jar;
K:\jdk\jre\lib\ext\localedata.jar;
K:\jdk\jre\lib\ext\nashorn.jar;
K:\jdk\jre\lib\ext\sunec.jar;
K:\jdk\jre\lib\ext\sunjce_provider.jar;
K:\jdk\jre\lib\ext\sunmscapi.jar;
K:\jdk\jre\lib\ext\sunpkcs11.jar;
K:\jdk\jre\lib\ext\zipfs.jar;
K:\jdk\jre\lib\javaws.jar;
K:\jdk\jre\lib\jce.jar;
K:\jdk\jre\lib\jfr.jar
K:\jdk\jre\lib\jfxswt.jar;
K:\jdk\jre\lib\jsse.jar;
K:\jdk\jre\lib\management-agent.jar;
K:\jdk\jre\lib\plugin.jar;
K:\jdk\jre\lib\resources.jar;
K:\jdk\jre\lib\rt.jar;
E:\Java\JavaSE\out\production\java-ch02;
E:\Java\JavaSE\java-ch02\src\com\wu\lib\commons-io-2.8.0.jar;
F:\idea\IntelliJ IDEA 2019.3.3\lib\idea_rt.jar
*/
}
}
通过反射获取运行时类的完整结构
package com.wu.reflection;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
//获得类的信息
public class Test08 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException {
Class c1 = Class.forName("com.wu.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);
}
//获得指定属性的值
Field name = c1.getDeclaredField("name");
System.out.println(name);
//获得类的方法
System.out.println("------------------------");
Method[] methods = c1.getMethods();//获得本类及其父类的全部方法
for (Method method : methods) {
System.out.println("正常的:"+method);
}
methods = c1.getDeclaredMethods();//获得本类的所有方法
for (Method method : methods) {
System.out.println("getDeclaredMethods:"+method);
}
//获得指定的方法
Method getName = c1.getMethod("getName", null);
Method setName = c1.getMethod("setName", String.class);
System.out.println(getName);
System.out.println(setName);
//获得指定的构造器
System.out.println("------------------------");
Constructor[] constructors = c1.getConstructors();//获得public构造
for (Constructor constructor : constructors) {
System.out.println(constructor);
}
constructors = c1.getDeclaredConstructors();//获得全部构造
for (Constructor constructor : constructors) {
System.out.println("getDeclaredConstructors"+constructor);
}
//获得指定的构造器
Constructor declaredConstructor = c1.getDeclaredConstructor(String.class, int.class, int.class);
System.out.println("指定:"+declaredConstructor);
}
}
创建类的对象:调用Class对象的newlnstance()方法
思考?难道没有无参构造器就不能创建对象了吗?只要在操作的时候明确的调用类中的构造器,并将参数传递进去之后,才可以实例化操作。
package com.wu.reflection;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
//通过反射动态的创建对象
public class Test09 {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {
//获得Class对象
Class c1 = Class.forName("com.wu.reflection.User");
//构造一个对象
// User user = (User)c1.newInstance();//本质上是调用了一个无参构造器
// System.out.println(user);//User{name='null', id=0, age=0}
//通过构造器创建对象
// Constructor constructor = c1.getDeclaredConstructor(String.class, int.class, int.class);
// Object user2 = constructor.newInstance("wu", 1, 18);
// System.out.println("--------------");
// System.out.println(user2);//User{name='wu', id=1, age=18}
//通过反射调用普通方法
User user3 = (User)c1.newInstance();
//通过反射获取一个方法
Method setName = c1.getDeclaredMethod("setName", String.class);
//invoke:激活的意思
//(对象,”方法的值“)
setName.invoke(user3,"wu");
System.out.println(user3.getName());
//通过反射操作属性
System.out.println("-------------------");
User user4 = (User)c1.newInstance();
Field name = c1.getDeclaredField("name");
//不能直接操作私有属性,我们需要关闭程序的安全检测 即属性或者方法的setAccessible(true);
name.setAccessible(true);//关闭安全检测
name.set(user4,"wu2");
System.out.println(user4.getName());
}
}
Method和Field、Constructor对象都有setAccessible()方法
setAccessible作用是启动和禁用访问安全检查的开关
参数值为true则指示反射的对象在使用时应该取消Java语言访问检查
参数值为false则指示反射的对象应该实施Java语言访问检查
package com.wu.reflection;
import com.wu.annotation.Test01;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
//分析性能问题
public class Test10 {
//普通方式调用
public static void test01(){
User user = new User();
long startTime = System.currentTimeMillis();
for (int i = 0; i < 1000000000; i++) {
user.getName();
}
long endTime = System.currentTimeMillis();
System.out.println("普通方式执行十亿次:"+(endTime-startTime)+"ms");
}
//反射方式调用
public static void test02() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
User user = new User();
Class c1 = user.getClass();
Method getName = c1.getDeclaredMethod("getName", null);
long startTime = System.currentTimeMillis();
for (int i = 0; i < 1000000000; i++) {
getName.invoke(user,null);
}
long endTime = System.currentTimeMillis();
System.out.println("反射方式执行十亿次:"+(endTime-startTime)+"ms");
}
//反射方式调用,关闭检测
public static void test03() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
User user = new User();
Class c1 = user.getClass();
Method getName = c1.getDeclaredMethod("getName", null);
getName.setAccessible(true);//关闭权限检查
long startTime = System.currentTimeMillis();
for (int i = 0; i < 1000000000; i++) {
getName.invoke(user,null);
}
long endTime = System.currentTimeMillis();
System.out.println("反射并关闭检测方式执行十亿次:"+(endTime-startTime)+"ms");
}
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
test01();
test02();
test03();
/*
普通方式执行十亿次:4ms
反射方式执行十亿次:2756ms
反射并关闭检测方式执行十亿次:1362ms
*/
}
}