Annotaion
)是JDK5.0
开始引入的技术在java.lang
包下定义了3个注解
@Override - 检查该方法是否是重载方法。如果发现其父类,或者是引用的接口中并没有该方法时,会报编译错误。
@Deprecated - 标记过时方法。如果使用该方法,会报编译警告。
@SuppressWarnings - 指示编译器去忽略注解中声明的警告。与前面两个注解有所不同,该注解需要添加一个参数才能正确使用,这些参数都是已经定义好了的,我们选择性的使用就好。
@SuppressWarnings(“all”)
@SuppressWarnings(“unchecked”)@SuppressWarnings(value={“unchecked”,“deprecation”})
自定义一个注解
@interface myAnnotation {
String value();
}
使用[权限修饰符] @interface
自定义一个注解,自动继承java.lang.annotaion.Annotaion
接口
String value();
表面上是个方法,实际上是生命了一个配置参数,在使用时需要传值。
可以通过default
给参数进行默认赋值,在使用时如果没有默认值则必须进行传值。
@interface myAnnotation {
String value() default "";
}
只有一个成员时,一般参数名设置为value
注解的使用和传值
定义如下注解:
@interface myAnnotation {
int id();
String name();
String sex();
}
在一个类中使用注解
@ myAnnotation(id = 1001,name="tom",sex = "man")
class UserInfo{
}
1. 使用注解时通过`key=value`的形式传值
1. 如果不指定`key`,则默认传递给注解中的`value`参数。
元注解就是作用在注解上的注解,定义在java.lang.annotaion
包下。
@Target
:用开说明注解的使用范围。
源代码中的定义:
public @interface Target {
/**
* Returns an array of the kinds of elements an annotation interface
* can be applied to.
* @return an array of the kinds of elements an annotation interface
* can be applied to
*/
ElementType[] value();
}
源码中的ElementType
是一个枚举类型,定义在java.lang.annotation
中,此枚举类型的常量提供了注释可能出现在Java程序中的语法位置的简单分类。 这些常量用于Target
元注释,以指定写入给定类型注释的合法位置。
public enum ElementType {
TYPE, // 类、接口、枚举类
FIELD, // 成员变量(包括:枚举常量)
METHOD, // 成员方法
PARAMETER, // 方法参数
CONSTRUCTOR, // 构造方法
LOCAL_VARIABLE, // 局部变量
ANNOTATION_TYPE, // 注解类
PACKAGE, // 可用于修饰:包
TYPE_PARAMETER, // 类型参数,JDK 1.8 新增
TYPE_USE // 使用类型的任何地方,JDK 1.8 新增
使用
@Target({ElementType.METHOD,ElementType.ANNOTATION_TYPE})
@interface myAnnotation {
}
@Retention
:描述注解保留的时间范围,一共有三种策略,定义在RetentionPolicy枚举中:
public enum RetentionPolicy {
SOURCE, // 源文件保留
CLASS, // 编译期保留,默认值
RUNTIME // 运行期保留,可通过反射去获取注解信息
}
@Retention(RetentionPolicy.CLASS)
public @interface ClassPolicy {
Documented
注解的作用是:描述在使用 javadoc 工具为类生成帮助文档时是否要保留其注解信息。
@Inherited
表示允许子类继承父类中的注解。
在程序运行中动态地获取类的相关属性,同时调用对象的方法和获取属性,这种机制被称之为Java反射机制。
Java反射是Java实现动态语言的关键,也就是通过反射实现类动态加载
静态加载: 在编译时加载相关的类,如果找不到类就会报错,依赖性比较强
动态加载:在运行时加载需要的类,在项目跑起来之后,调用才会报错,降低了依赖性
例子:静态加载,如下代码,如果找不到类的情况,代码编译都不通过
User user = new User();
而动态加载,就是反射的情况,是可以先编译通过的,然后在调用代码时候,也就是运行时才会报错
Class> cls = Class.forName("com.example.core.example.reflection.User");Object obj = cls.newInstance();
java中的反射允许程序在执行期借助jdk中Reflection API来获取类的内部信息,比如成员变量、成员方法、构造方法等等,并能操作类的属性和方法
java中反射的实现和jvm和类加载机制有一定的关系,加载好类之后,在jvm的堆中会产生一个class类型的对象,这个class类包括了类的完整结构信息,通过这个class对象就可以获取到类的结构信息,所以形象地称之为java反射。
定义一个实体类
public class User {
private String name;
private int age;
private String sex;
public User() {
}
public User(String name, int age, String sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
", sex='" + sex + '\'' +
'}';
}
}
通过反射获取类的class
对象
public static void main(String[] args) throws ClassNotFoundException {
Class c1 = Class.forName("com.a001.User");
System.out.println(c1);//class com.a001.User
}
一个类在内存中只有一个Class
对象:
Class c1 = Class.forName("com.a001.User");
Class c2 = Class.forName("com.a001.User");
System.out.println(c1 == c1);//true
类被加载后,;类的整个结果都在Class
对象中。
Object
类中定义了getClass
方法,可以通过类的对象获取类的Class
对象。
public static void main(String[] args) throws ClassNotFoundException {
Class c1 = Class.forName("com.a001.User");
Class c2 = new User().getClass();
System.out.println(c1 == c2);//true
}
每一个类都有class
属性,通过类名.class
属性获得Class
的对象(比较高效)
public static void main(String[] args) throws ClassNotFoundException {
Class c1 = com.a001.User.class
}
基本包装类型包装类都有一个TYPE
属性
public static void main(String[] args) throws ClassNotFoundException {
Class c1 = Intger.TYPE
}
Class
对象Class
对象存储了一个类的相关信息
Class
本身也是个类,但对应的对象不是被new
出来的,而是系统创建的。
一个Class
对象对应一个加载到JVM
中的一个.class
文件
每个类的实例都划记得自己由哪个Class
实例生成的,可以通过getClass
方法生成。
Class
类中提供的方法
那些类型可以有Class
对象
Class c1=Object.class;
Class c2=Comparable.class;
Class c3=int[][].class;
Class c4= ElementType.class;
Class c5=Target.class;
Class c6=Integer.class;
Class c7=void.class;
Class c8=Class.class;
类的加载大致过程
.class
文件读入内存,并将这件静态数据转换成方法区的运行时数据,并为之创建一个Class
对象代表这个类。JRE
中。
static
)分配内存并设置初始值,这些内存在方法区中分配JVM
负责对类进行初始化。
clinit()
方法的过程,其方法体是所有类中的 static
成员合并而成的(相同的代码会被替换)。clinit()
方法在多线程中被正确加锁和同步。类初始化的时机
main
方法所在的类new
一个类的对象final
常量)和静态方法java.lang.reflect
包中的方法对类进行反射调用类加载器
类加载器的作用:将.class
文件读入内存,并将这件静态数据转换成方法区的运行时数据,并为之创建一个Class
对象代表这个类,==该对象作为作为访问方法区类数据的入口。==JVM预定义有三种类加载器,当一个 JVM启动的时候,Java开始使用如下三种类加载器:
根类加载器(bootstrap class loader):它用来加载 Java 的核心类,由于引导类加载器涉及到虚拟机本地实现细节,开发者无法直接获取到启动类加载器的引用,所以不允许直接通过引用进行操作。
在lib
目录下找到rt.jar
,并用解压软件打开,里面java/long
目录下就是对应的java.long
包,java
目录下还能看到其他常用的类库(Math
,io
,time
)。根类加载器就是加载这些包的。
扩展类加载器(extensions class loader):它负责加载JRE
的扩展目录,==lib/ext
==或者由java.ext.dirs
系统属性指定的目录中的JAR包
的类。由Java语言实现,父类加载器为null
。
系统类加载器(system class loader):被称为系统(也称为应用)类加载器,它负责在JVM启动时加载来自Java命令的-classpath
选项、java.class.path
系统属性,或者CLASSPATH
换将变量所指定的JAR包和类路径。程序可以通过ClassLoader
的静态方法getSystemClassLoader()
来获取系统类加载器。如果没有特别指定,则用户自定义的类加载器都以此类加载器作为父加载器。由Java语言实现,父类加载器为ExtClassLoader
。
// 测试某个类是由哪个类加载器加载的
ClassLoader classLoader1 = ClassLoaderTest.class.getClassLoader();
System.out.println(classLoader1);//jdk.internal.loader.ClassLoaders$AppClassLoader@251a69d7
ClassLoader classLoader2 = Object.class.getClassLoader();
System.out.println(classLoader2);//null 因为无法直接获取
//获得系统加载类在那些路径加载类的
System.out.println(System.getProperty("java.class.path"));
class User {
private String name;
private int age;
private String sex;
public User() {}
public User(String name, int age, String sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
@Override
public String toString() {
return "User{" + "name='" + name + '\'' + ", age=" + age + ", sex='" + sex + '\'' + '}';
}
}
Class c1 = User.class;
System.out.println(c1.getName()); // com.a001.User
System.out.println(c1.getSimpleName()); // User
Field[] field = c1.getFields();
System.out.println(Arrays.toString(field));
Field[] declaredFields = c1.getDeclaredFields(); // []
System.out.println(Arrays.toString(declaredFields));
// [private java.lang.String com.a001.User.name, private int com.a001.User.age, private
// java.lang.String com.a001.User.sex]
Field name = c1.getDeclaredField("name");
System.out.println(name); // private java.lang.String com.a001.User.name
获取所有的public属性,包括继承过来的 | 获取在本类声明的所有属性 | |
---|---|---|
获取满足条件的所有属性 | getFields() |
getDeclaredFields() |
根据属性名获取属性 | getField(String name) |
getDeclaredField(String name) |
Method[] methods = c1.getMethods();
System.out.println(Arrays.toString(methods));
// [public java.lang.String com.a001.User.toString(), public final void
// java.lang.Object.wait(long,int) throws java.lang.InterruptedException, public final void
// java.lang.Object.wait() throws java.lang.InterruptedException, public final native void
// java.lang.Object.wait(long) throws java.lang.InterruptedException, public boolean
// java.lang.Object.equals(java.lang.Object), public native int java.lang.Object.hashCode(),
// public final native java.lang.Class java.lang.Object.getClass(), public final native void
// java.lang.Object.notify(), public final native void java.lang.Object.notifyAll()]
Method[] declaredMethods = c1.getDeclaredMethods();
System.out.println(Arrays.toString(declaredMethods));
//[public java.lang.String com.a001.User.toString()]
获取所有的public方法,包括继承过来的 | 获取在本类声明的所有方法,包括重写的 | |
---|---|---|
获取满足条件的所有方法 | getMethods() |
getDeclaredMethods() |
根据方法名获取方法 | getMethod(String name,*参数列表) |
getDeclaredMethod(String name,*参数列表) |
注; 方法名+参数列表才能定位到具体的方法,尤其是对于重载的方法而言
public class Main {
public static void main(String[] args) throws NoSuchFieldException, NoSuchMethodException {
Class c1 = Test.class;
System.out.println(c1.getDeclaredMethod("func", int.class, String.class));
//public void com.a001.Test.func(int,java.lang.String)
}
}
class Test {
public void func(int a) {}
public void func(String b) {}
public void func(int a, String b) {}
}
获取在本类声明的所有的public构造器 | 获取在本类声明的所有构造器 | |
---|---|---|
获取满足条件的所有方法 | getMethods() |
getDeclaredMethods() |
根据方法名获取方法 | getMethod(String name,*参数列表) |
getDeclaredMethod(String name,*参数列表) |
Class userClass = User.class;
System.out.println(Arrays.toString(userClass.getConstructors()));
//[public com.a001.User(), public com.a001.User(java.lang.String,int,java.lang.String)]
System.out.println(Arrays.toString(userClass.getDeclaredConstructors()));
//[public com.a001.User(), public com.a001.User(java.lang.String,int,java.lang.String)]
System.out.println(userClass.getDeclaredConstructor(String.class, int.class, String.class));
//public com.a001.User(java.lang.String,int,java.lang.String)
newInstance()
Class userClass = User.class;
// 创建一个对象
User user1 = (User) userClass.newInstance();
System.out.println(user1);//User{name='null', age=0, sex='null'}
/*
分析:1. newInstance通过调用无参构造器创建了对象
2. 该方法已经弃用
*/
通过获得类的构造器来创建对象
Class userClass = User.class;
// 获取指定的构造器
Constructor declaredConstructor = userClass.getDeclaredConstructor(String.class, int.class, String.class);
//根据构造器创建对象
User user2 = (User) declaredConstructor.newInstance("Tom", 20, "man");
System.out.println(user2); //User{name='Tom', age=20, sex='man'}
Class userClass = User.class;
User user = new User("tom", 12, "man");
// 获得对应的方法
Method toString = userClass.getDeclaredMethod("toString", null);
// 传入指定的对象和参数值(若有)调用方法 invoke(翻译:激活)
System.out.println(toString.invoke(user));
//User{name='tom', age=12, sex='man'}
Class userClass = User.class;
User user = new User("tom", 12, "man");
Field name = userClass.getDeclaredField("name");
name.setAccessible(true); // 关闭权限检测,可以对私有属性进行修改
name.set(user, "tom2");
System.out.println(name.get(user));
总结:
操作 | 方法 |
---|---|
通过构造器创建实例 | constructor.newInstance() |
调用方法 | method.invoke() |
获取、修改属性 | field.get() field.get() (对私有属性要关闭权限检查) |
Java
中采用范型擦除机制,即范型仅在编译阶段使用,一旦编译完成后,所有和范型相关的类型全部擦除掉。
为了在反射中操作这些范型型,java
新增了ParameterizedType
,GenericArrayType
、TypeVariable
和WildcardType
等操作范型的类:
public class Main {
public static void main(String[] args) throws NoSuchMethodException {
Class c1 = Main.class;
// 获得方法
Method test = c1.getMethod("Test", Map.class, List.class);
System.out.println(test);
// public static java.util.ArrayList com.a001.Main.Test(java.util.Map,java.util.List)
/*
分析:修饰符 public static
返回值: java.util.ArrayList
方法名 com.a001.Main.Test
参数列表 java.util.Map,java.util.List
*/
// 获得方法中参数列表中的范型信息
Type[] genericParameterTypes = test.getGenericParameterTypes();
System.out.println(Arrays.toString(genericParameterTypes));
// [java.util.Map, java.util.List]
// 获得具体的范型
for (Type genericParameterType : genericParameterTypes) {
// 判断获取的类型是不是参数化类型
if (genericParameterType instanceof ParameterizedType) {
// 强制转化成真实类型
Type[] actualTypeArguments =
((ParameterizedType) genericParameterType).getActualTypeArguments();
for (Type actualTypeArgument : actualTypeArguments) {
System.out.println(actualTypeArgument);
/*
class java.lang.String
class java.lang.String
class java.lang.Integer
*/
}
}
}
}
public static ArrayList<Character> Test(Map<String, String> map, List<Integer> list) {
return new ArrayList<Character>();
}
}
定义注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface TableUser {
String value();
}
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface FiledUser {
String colName();
String type();
int length();
}
定义类,并使用注解
@TableUser("db_user")
public class User {
@FiledUser(colName = "db_id",type = "int",length =4)
private int id;
@FiledUser(colName = "db_name",type = "varchar",length =3)
private String name;
@FiledUser(colName = "db_age",type = "int",length =2)
private int age;
public User() {
}
public User(int id, String name, int age) {
this.id = id;
this.name = name;
this.age = age;
}
}
获取注解信息
public class Main {
public static void main(String[] args) throws NoSuchFieldException {
Class classUser = com.a001.User.class;
// 获得类上的注解
Annotation[] annotations = classUser.getAnnotations();
System.out.println(Arrays.toString(annotations));
// [@com.a001.TableUser("db_user")]
// 获得指定的注解
TableUser annotation = (TableUser) classUser.getAnnotation(TableUser.class);
// 获取注解中对应的valud值
System.out.println(annotation.value()); //db_user
//获取属性上的注解
//1. 获取属性
Field id = classUser.getDeclaredField("id");
// 2. 根据属性获得对应的注解
FiledUser annotation1 = id.getAnnotation(FiledUser.class);
System.out.println(annotation1.colName());
System.out.println(annotation1.type());
System.out.println(annotation1.length());
/*
db_id
int
4
*/
}
}