文章目录
一.注解
1.什么是注解
目的是为了用反射去读
Annotation的作用:
不是程序本身,可以对程序作出解释.(这一点和注释(comment)没什么区别)
可以被其他程序比如:编译器等)读取.
Annotation的格式:
注解是以"@注释名"在代码中存在的,还可以添加一些参数值,例
如:@SuppressWarnings(value=“unchecked”),
Annotation在哪里使用?可以附加在package,class,method,field等上面,相当于给他们添加了额外的辅助信息,
我们可以通过反射机制编程实现对这些元数据的访问
2.内置注解
@Override(重写) | 表示打算重写超类中的方法声明 |
---|---|
@Deprecated (不赞成) | 表示不鼓励程序员使用这样的元素,通常是因为他很危险或有更好的选择。 |
@SuperWarnings(超级警告?) | 用法@SuperWarnings(“finally”) |
---|---|
deprecation (不赞成) | 使用了过时的类或方法的警告 |
unchecked(未加抑制的) | 执行了未检查的转换时的警告 如:使用集合时未指定泛型 |
fallthrough (未经检查的) | 当在switch语句使用时发生case穿透 |
path(路径) | 在类路径、源文件路径中有不存在路径的警告 |
serial(连续的) | 当在序列化的类上缺少serialVersionUID定义时的警告 |
finally(最后) | 任何finally子句不能完成时的警告 |
all (所有) | 关于以上所有的警告 |
3.元注解
这些类型和它们所支持的类在java.lang.annotation包中可以找到.
(@Target,@Retention,@Documented,@Inherited
@Target:用于描述注解的使用范围(即:被描述的注解可以用在什么地方)
@Retention:表示需要在什么级别保存该注释信息,用于描述注解的生命周期(SOURCE <CLASS <RUNTIME)
@Document:说明该注解将被包含在javadoc中
@Inherited:说明子类可以继承父类中的该注解
package 注解和反射;
import java.lang.annotation.*;
public class 元注解 {
@myannotation
public void test()
{
}
}
@Target(value = {ElementType.METHOD})//表示我们的注解可以用在那些地方
@Retention(value = RetentionPolicy.RUNTIME)//一般默认为runtime,
@Documented//表示时候将我们的注解生成在javadoc中
@Inherited//表示可以继承父类的注解
@interface myannotation{
}
package 注解和反射;
import java.lang.annotation.*;
public class 元注解 {
@myannotation
public void test()
{
}
}
@Target(value = {ElementType.METHOD})//表示我们的注解可以用在那些地方
@Retention(value = RetentionPolicy.RUNTIME)//一般默认为runtime,
@Documented//表示时候将我们的注解生成在javadoc中
@Inherited//表示可以继承父类的注解
@interface myannotation{
}
3.自定义注解
使用@interface自定义注解时,自动继承了java.lang.annotation.Annotation接口
其中的每一个方法实际上是声明了一个配置参数
方法的名称就是参数的名称
返回值类型就是参数的类型(返回值类型只能是基本类型、Class、String、enum)
可以通过default来声明参数的默认值
如果只有一个参数成员,一般参数名为value
我们在使用注解元素时必须要有值,可以定义默认值,空字符串,0或者-1
public @interface TestAnnotation {
//参数默认为空
String value() default “”;
}
default(默认值)
package 注解和反射;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
public class 自定义注解 {
@my(name = "如果有了就不用")//如果没有默认值就必须给他赋值。
public void teat()
{}
}
@Target({ElementType.ANNOTATION_TYPE.TYPE,ElementType.ANNOTATION_TYPE.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface my{
//注解的参数:参数类型+参数名();
String name() ;
int age()default 0;
int id()default -1;//如果默认值为-1,代表找不到
String []schools()default {"ffffff"};
}
二.反射Reflection
在加载完类后,在堆内存的方法区就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象就包含了完整的类的结构信息,我们可以通过这个对象看到类的结构,这个对象就像一面镜子,透过这个镜子看到类的结构,所以我们形象的称之为:反射
动态语言与静态语言
动态语言
动态语言是一类在运行时可以改变其结构的语言:例如新的函数,对象,甚至代码可以被引进,已有的函数可以被删除或是其它结构上的变化。通俗点说就是在运行时代码可以根据某些条件改变自身结构
主要的动态语言有:Object-c、C#、JavaScript、PHP、Python等
静态语言
与动态语言相比,运行时结构不可变的语言就是静态语言。例如Java、C、C++
Java不是动态语言,但是Java可以称为“准动态语言”。即Java有一定的动态性,我们可以利用反射机制来获取类似于动态语言的
特性,Java的动态性让编程的时候更加灵活。
优缺点:
优点:
可以实现动态创建对象和编译,体现出很大的灵活性
缺点:
对性能有影响。使用反射基本上是一种解释操作,我们可以告诉JVM,
我们希望做什么并且它满足我们的要求。这类操作总是慢于直接典行相同的操作
class User {
//这里name用的是私有类型
private String name;
//这里age用的是公有类型
public int age;
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
//无参构造器
public User(){}
//有参构造器
public User(String name, int age) {
this.name = name;
this.age = age;
}
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;
}
}
}
}
}
}
获取class对象
(1)类的 class 属性
每一个类,都有一个 class 静态属性,这个静态属性就是类对应的 Class 对象。
1 Class<Person> cl1 = Person.class;
(2)Object 对象 的 getClass() 方法
1 Person p1 = new Person();
2 Class<Person> cl2 = (Class<Person>) p1.getClass();
(3)通过 Class 类的 forName() 方法(最常用)
1 try {
2 Class cl3 = Class.forName("com.llm.hkl.Person");
3 } catch (ClassNotFoundException e) {
4 e.printStackTrace();
5 }
(4)通过 ClassLoader 类(不常用)
1 ClassLoader cl = Person.class.getClassLoader();
2 try {
3 Class cl4 = cl.loadClass("com.llm.hkl.Person");
4 } catch (ClassNotFoundException e) {
5 e.printStackTrace();
6 }
它们的hashcode码是一样的,这就说明了:
一个类在内存中只有一个Class对象
一个类被加载后,类的整体结构都会被封装在Class对象中
class常用方法
ClassforName(String name):返回指定类name的Class对象
newInstance():调用缺省构造函数,返回Class对象的一个实例
getName():返回此Class对象所表示的实体(类,接口,数组或void)的名称
getSuperClass():返回当前Class对象的父类Class对象
getinterfaces():返回当前对象的接口
getClassLoader():返回该类的类加载器
getConstructors():返回一个包含某些Constructor对象的数组
getMethod(String name, Class… T):返回一个Method对象,此对象的形参类型为paramsType
getDeclaredFields():返回Field对象的一个数组
java内存分析
加载:将class文件字节码内容加载到内存,并将这些静态数据转换成方法区的运行时数据结构,然后生成一个代表这个类的 java.lang.Class 对象。
链接:将Java类的二进制代码合并到JVM的运行状态之中的过程。
验证:确保加载的类信息符合JVM规范,没有安全方面的问题
准备:正式为类变量(static)分配内存并设置类变量默认初始值的阶段,这些内存都将在方法区中进行分配。
解析:虚拟机常量池的符号引用(常量名)替换为直接引用(地址)的过程
初始化:
执行类构造器方法的过程,类构造器
方法是由编译期自动收集类中所有类变量的赋值动作和静态代码块中的语句合并产生的。(类构造器是构造类信息的,不是构造该类对象的构造器)当初始化一个类的时候,如果发现其父类还没有初始化完成,则需要先触发其父类的初始化
虚拟机会保证一个类的方法在多相差环境中被正确的加锁和同步
class SuperA {
static {
System.out.println("父类静态代码块初始化");
}
public SuperA() {
System.out.println("父类构造函数初始化");
}
}
class A extends SuperA{
static {
System.out.println("静态代码块初始化");
m = 300;
}
static int m = 100;
public A() {
System.out.println("A类的无参构造方法");
}
}
public class ClassLoaderDemo {
public static void main(String[] args) {
A a = new A();
System.out.println(a.m);
}
}
最后的结果为:
父类静态代码块初始化
静态代码块初始化
父类构造函数初始化
A类的无参构造方法
100
什么时候发生类初始化
类的主动引用(一定发生初始化)
- 当虚拟机启动,先初始化main方法所有在类new 一个类的对象
调用类的静态成员(除了 final常量)和静态方法使用 java.lang.reflect包的方法对类进行反射调用当初始化一个类,如果其父类没有被初始化,则会先初始化 - 的父类类的被动引用(不会发生初始化)
当访问一个静态域时,只有真正的申明这个域的类才会被初始化,如:当通过子类引用父类的静态变量,不会导致子类初始化通过数组定义类引用,不会触发此类的初始化引用常量不会触发此类的初始化(常量在链接阶段就存入调用类的常量池了)。
类加载器的作用
类加载的作用:将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时数据结构,然后在堆中生成了一个代表这个类的 java.lang.Class对象,作为方法区中类数据的访问入口。
类缓存:标准的JavaSE类加载器可以按要求查找类,但是一旦某个类被加载到类加载器中,它将维持加载(缓存)一段时间。不过JVM垃圾回收机制可以回收这些Class对象
public class ClassLoaderTypeDemo {
public static void main(String[] args) {
//当前类是哪个加载器
ClassLoader loader = ClassLoaderTypeDemo.class.getClassLoader();
System.out.println(loader);
// 获取系统类加载器
ClassLoader classLoader = ClassLoader.getSystemClassLoader();
System.out.println(classLoader);
// 获取系统类加载器的父类加载器 -> 扩展类加载器
ClassLoader parentClassLoader = classLoader.getParent();
System.out.println(parentClassLoader);
// 获取扩展类加载器的父类加载器 -> 根加载器(C、C++)
ClassLoader superParentClassLoader = parentClassLoader.getParent();
System.out.println(superParentClassLoader);
// 测试JDK内置类是谁加载的
ClassLoader loader2 = Object.class.getClassLoader();
System.out.println(loader2);
}
}
运行结果:我们发现,根加载器我们无法获取到
sun.misc.Launcher$AppClassLoader@18b4aac2
sun.misc.Launcher$AppClassLoader@18b4aac2
sun.misc.Launcher$ExtClassLoader@45ee12a7
null
null
reflect用法
哪些类型可以有Class对象
class:外部类,成员(成员内部类,静态内部类),局部内部类,匿名内部类
interface:接口
[]:数组
enum:枚举
annotation:注解@interface
primitive type:基本数据类型
public class GetClassDemo {
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 java.lang.Object
interface java.lang.Comparable
class [Ljava.lang.String;
class [[I
interface java.lang.Override
class java.lang.annotation.ElementType
class java.lang.Integer
void
class java.lang.Class
void
获取类的基本信息
不带Declared的方法职能获取到public字段,且包括父类的,带Declared的方法可以获取到所有的自定义的字段(包括私有)!
获得类的名字
获得类的属性
获得指定属性的值
获得类的方法
获得指定方法
获取构造器
获得指定的构造器
Class<?> person = Class.forName("Person");
//获得类的名字
System.out.println("获得类的名字-------------------------->");
String name = person.getName();
System.out.println(name);
//获得类的属性
System.out.println("获得类的属性--------------------->");
//获取该类的所有public字段,包括父类的
System.out.println("getFields----------------->");
Field[] fields = person.getFields();
for (Field field : fields) {
System.out.println(field);
}//获取该类的所有字段,不包括父类(仅自定义)
System.out.println("<-----------------getDeclaredFields----------------->");
Field[] declaredFields = person.getDeclaredFields();
for (Field declaredField : declaredFields) {
System.out.println(declaredField);
}
//获得指定属性的值
System.out.println("<--------------------获得类的指定属性--------------------------------------->");
Field name1 = person.getDeclaredField("name");
System.out.println(name1);
//获得类的方法
System.out.println("获得类的方法--------------------------------------->");
//,获取本类极其父类所有public方法。
System.out.println("getMethods----------------->");
Method[] methods = person.getMethods();
for (Method method : methods) {
System.out.println(method);
}
System.out.println("getDeclaredMethods----------------->");//,获取本类极其所有public方法(包括私有)。
Method[] declaredMethods = person.getDeclaredMethods();
for (Method declaredMethod : declaredMethods) {
System.out.println(declaredMethod);
}
//获得指定方法
System.out.println("<-------------------------获得类的指定方法--------------------------------------->");
Method say = person.getDeclaredMethod("say",String.class);
System.out.println(say);
//获取构造器
System.out.println("<--------------------------获得类的构造器--------------------------------->");
System.out.println("<-----------------getConstructors----------------->");
//仅public
Constructor<?>[] constructors = person.getConstructors();
for (Constructor<?> constructor : constructors) {
System.out.println(constructor);
}
System.out.println("<-----------------getDeclaredConstructors----------------->");
Constructor<?>[] declaredConstructors = person.getDeclaredConstructors();
for (Constructor<?> declaredConstructor : declaredConstructors) {
System.out.println(declaredConstructor);
}
//获得指定的构造器
System.out.println("获得类指定的构造器------------------------------>");
Constructor<?> declaredConstructor = person.getDeclaredConstructor(String.class, String.class, int.class);
System.out.println(declaredConstructor);
获取类的字段,变量,类的方法,构造器
Class aClass = Class.forName("sml.reflect.User");
//获取该类的所有public字段,包括父类的
Field[] fields = aClass.getFields();
//根据字段名获取该类的public字段
Field field = aClass.getField("age");
//获取该类的所有字段,不包括父类(仅自定义)
Field[] fields1 = aClass.getDeclaredFields();
//根据字段名获取该类的字段
Field field1 = aClass.getDeclaredField("name");
Class aClass = Class.forName("sml.reflect.User");
//获取该类的所有public方法,包括父类的
Method[] methods = aClass.getMethods();
//根据方法名获取该类的public方法
Method method = aClass.getMethod("getName");
//如果该类为重写方法,可以在第二个参数加上重写方法的参数类型,不写为无参数的方法
Method paramMethod = aClass.getMethod("getName",String.class)
//获取该类的所有方法,不包括父类(仅自定义)
Method[] declaredMethods = aClass.getDeclaredMethods();
//根据方法名获取该类的方法
Method declaredMethod = aClass.getDeclaredMethod("getName");
获取方法的方式与获取字段的方法一样,在这里我们需要注意的是重写的方法,一个类中存在俩个或多个方法名是一样的,因此在根据方法名获取方法时,提供第二个参数,为可变参数。参数类型为我们获取方法的参数类型的类,如果不写默认为无参方法。
Class aClass = Class.forName("sml.reflect.User");
//获取该类的所有构造器,包括父类
Constructor[] constructors = aClass.getConstructors();
//根据构造器的参数类型来获取指定构造器,不写为无参构造器
Constructor constructor = aClass.getConstructor();
Constructor constructor1 = aClass.getConstructor(String.class,int.class);
//获取该类的所有构造器,不包括父类
Constructor[] declaredConstructors = aClass.getDeclaredConstructors();
//根据构造器的参数类型来获取指定的自定义构造器,不写为无参构造器
Constructor declaredConstructor = aClass.getDeclaredConstructor();
Constructor declaredConstructor1 = aClass.getDeclaredConstructor(String.class, int.class);
实例化对象
默认获取的是Object类型,因此最后需要进行下类型转化。
public class GetObjectByReflectionDemo {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {
// 获取Class
Class clazz = Class.forName("com.moxi.interview.study.annotation.User");
// 构造一个对象,newInstance调用的是无参构造器,如果没有无参构造器的话,本方法会出错
// User user = (User)clazz.newInstance();
// 获取class的有参构造器
Constructor constructor = clazz.getDeclaredConstructor(String.class, int.class, int.class);
User user2 = (User) constructor.newInstance("小名", 10, 10);
System.out.println(user2);
// 通过反射调用普通构造方法
User user3 = (User)clazz.newInstance();
// 获取setName 方法
Method setName = clazz.getDeclaredMethod("setName", String.class);
// 执行setName方法,传入对象 和 参数
setName.invoke(user3, "大名");
System.out.println(user3);
System.out.println("============");
Field age = clazz.getDeclaredField("age");
// 关闭权限检测,这样才能直接修改字段,因为 set方法不能直接操作私有变量
age.setAccessible(true);
age.set(user3, 10);
System.out.println(user3);
}
}
运行结果
User{name='小明', id=10, age=10}
User{name='大明', id=0, age=0}
============
User{name='小白', id=0, age=10}
完整结构
public class GetClassInfo {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException {
Class clazz = Class.forName("com.moxi.interview.study.annotation.User");
// 获取类名字
System.out.println(clazz.getName()); // 包名 + 类名
System.out.println(clazz.getSimpleName()); // 类名
// 获取类属性
System.out.println("================");
// 只能找到public属性
Field [] fields = clazz.getFields();
// 找到全部的属性
Field [] fieldAll = clazz.getDeclaredFields();
for (int i = 0; i < fieldAll.length; i++) {
System.out.println(fieldAll[i]);
}
// 获取指定属性的值
Field name = clazz.getDeclaredField("name");
// 获取方法
Method [] methods = clazz.getDeclaredMethods(); // 获取本类和父类的所有public方法
Method [] methods2 = clazz.getMethods(); // 获取本类所有方法
// 获得指定方法
Method method = clazz.getDeclaredMethod("getName", null);
// 获取方法的时候,可以把参数也丢进去,这样因为避免方法重载,而造成不知道加载那个方法
Method method2 = clazz.getDeclaredMethod("setName", String.class);
}
}
反射操作泛型
Java采用泛型擦除机制来引入泛型,Java中的泛型仅仅是给编译器Java才使用的,确保数据的安全性和免去强制类型转换的问题,但是一旦编译完成后,所有的泛型有关的类型全部被擦除
ParameterizedType,GenericArrayType,TypeVariable和WildcardType几种类型来代表不能被归一到Class类中的类型但是有何原始类型齐名的类型。
ParameterizedType:表示一种参数化类型,比如Collection
GenericArrayType:表示一种元素类型是参数化类型或者类型变量的数组类型
TypeVariable:是各种类型变量的公共父接口
WildcardType:代表一种通配符类型的表达式
public class GenericityDemo {
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 Exception{
Method method = GenericityDemo.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 method2 = GenericityDemo.class.getMethod("test02", null);
Type returnGenericParameterTypes = method2.getGenericReturnType();
// 遍历打印全部泛型
if(returnGenericParameterTypes instanceof ParameterizedType) {
Type[] actualTypeArguments = ((ParameterizedType) returnGenericParameterTypes).getActualTypeArguments();
for (Type actualTypeArgument : actualTypeArguments) {
System.out.println(actualTypeArgument);
}
}
}
}
class java.lang.String
class com.moxi.interview.study.annotation.User
# java.util.List<com.moxi.interview.study.annotation.User>
class com.moxi.interview.study.annotation.User
###################
class java.lang.String
class com.moxi.interview.study.annotation.User
反射操作注解
getAnnotation
getAnnotations
ORM即为:Object relationship Mapping,对象关系映射
类和表结构对应
属性和字段对应
对象和记录对应
class Student2 {
@FieldKuang(columnName = "db_id", type="int", length = 10)
private int id;
@FieldKuang(columnName = "db_age", type="int", length = 10)
private int age;
@FieldKuang(columnName = "db_name", type="varchar", length = 10)
private String name;
public Student2() {
}
public Student2(int id, int age, String name) {
this.id = id;
this.age = age;
this.name = 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 "Student2{" +
"id=" + id +
", age=" + age +
", name='" + name + '\'' +
'}';
}
}
/**
* 自定义注解:类名的注解
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface TableKuang {
String value();
}
/**
* 自定义注解:属性的注解
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface FieldKuang {
String columnName();
String type();
int length() default 0;
}
public class ORMDemo {
public static void main(String[] args) throws Exception{
// 获取Student 的 Class对象
Class c1 = Class.forName("com.moxi.interview.study.annotation.Student2");
// 通过反射,获取到全部注解
Annotation [] annotations = c1.getAnnotations();
for (Annotation annotation : annotations) {
System.out.println(annotation);
}
// 获取注解的value值
TableKuang tableKuang = (TableKuang)c1.getAnnotation(TableKuang.class);
String value = tableKuang.value();
System.out.println(value);
// 获得类指定的注解
Field f = c1.getDeclaredField("name");
FieldKuang fieldKuang = f.getAnnotation(FieldKuang.class);
System.out.println(fieldKuang.columnName());
System.out.println(fieldKuang.type());
System.out.println(fieldKuang.length());
}
}