标签:收集 方法 定义类 run href 使用 类对象 coding 小结
基于B站秦疆老师的课. 感谢!
@since JDK1.5
不是程序本身, 可以对程序做出解释, 可以被其他程序读取(如 编译器)
@Override
@SuppressWarnings(value="all") //"unchecked" ...
抑制编译器的警告信息
@Deprecated
不鼓励使用被修饰的方法\属性\类, 即将弃用
meta-annotation
给注解作注解
在java.lang.annotation, Java定义了4个标准的元注解:
@Target
: 描述注解的使用范围, 如类\方法或构造器等
@Retention
: 描述需要在什么级别保留该注解信息
? SOURCE < CLASS < RUNTIME
, 框架里很多用RUNTIME
@Documented
: 表示该注解讲被保留在Javadoc中
@Inherited
: 表示子类可以继承父类中的该注解
简单代码演示:
@Target(value = {ElementType.METHOD, ElementType.TYPE}) // 描述注解可以作用的语句类型
public @interface MyAnnotation{ // 自定义注解的声明语句
}
value
, 使用时可以免写参数名default
关键字修饰默认值-1
则表示不存在课程内容梗概
Java是准动态语言
因为它有反射机制, 才可以视之为"准动态语言"
从内存角度理解反射
类加载完毕后, 在堆内存的方法区就产生了一个唯一Class类型的对象, 其中包含了完整的类的结构信息. 我们可以通过它看到类的完整结构
反射对性能有一定影响
因为反射是一种"解释操作", 总是慢于"直接执行"
对Class类的认识
// 数组
int[] a = new int[10];
int[] b = new int[100];
// hashcode打印结果相等
460141958
460141958
哪些可以拥有Class对象?
Class<Object> c1 = Object.class; // 类
Class<Comparable> c2 = Comparable.class; // 接口
Class<String[]> c3 = String[].class; // 数组 class [Ljava.lang.String;
Class<int[][]> c4 = int[][].class; // 数组 class [[I
Class<Override> c5 = Override.class; // 注解
Class<ElementType> c6 = ElementType.class; // 枚举
Class<Integer> c7 = Integer.class; // 基本数据类型
Class<Integer> c72 = Integer.TYPE; // 基本数据另一种
Class<Void> c8 = void.class; // void
Class<Class> c9 = Class.class; // Class
// system out结果
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
int
void
class java.lang.Class
获取Class对象的方式
// 已有对象. Object超类提供的
public final Class getClass();
对象.getClass();
// Class类的静态方法
Class.forName("包名.类名");
// 已知类名, 最安全可靠, 性能高######################
类名.class();
// 基本数据类型的包装类, 可以直接用类名.TYPE
Integer.TYPE; // int
// 也可以通过ClassLoader
Class类的常用方法
static ClassforNae(String name); // 传入类名, 返回Class对象
Object newInstance(); // 调用缺省构造器, 返回返回Class对象的一个实例
getName(); // 返回此Class对象所表示的实体的名称, (如类\接口\数组类\void)
Class getSuperClass(); // 返回父类Class对象
Class[] getinterfaces(); // 获取当前Class对象的接口
ClassLoadder getClassLoader(); //获取该类的类加载器
Constructor[] getConstructors(); // 获取构造器对象们, 以数组保存
Method getMethod(String name, Class... T); // 返回一个Method对象, 对象形参为paramType
Field[] getDeclaredFields(); // 返回属性对象们, 以数组保存
内存的基本结构
Load
加载类的过程
static
数据转换成方法区的运行时数据Link
链接过程, 将类的二进制数据合并到JVM的运行状态, static变量赋默认初始化值, 常量赋指定值, 具体为:
验证: 确保所加载的类信息符合JVM规范
准备: 为static的数据()分配位于方法区的内存空间, 并设置默认初始 (例子中int m = 0
)
解析: JVM常量池中的符号引用 (常量名) 替换为直接引用 (地址)
[不太懂, 估计是常量赋指定常量值]
Initialize
执行"类构造器"<clinit>()
方法
另外有说明:
初始化类需要先初始化该类的父类
JVM会保证<clinit>()
方法在多线程环境中被正确加锁和同步
<clinit>()
方法:
- 是由编译器自动收集的类中的所有类变量的赋值动作和静态代码块中的语句合并产生的. [个人理解: 所有static修饰的数据]
- 是构造类信息的"类构造器" [个人理解: 不是constructor构造器!]
类加载过程, 自己理解的小结:
什么时候类会初始化?
结论: "类的主动引用", 会初始化类; "类的被动引用", 不会初始化类
主动引用
JVM启动, main()
所在类
new 对象
注: 如果有父类, 需要先初始化父类
类名直接调用static成员
注: 不包括static final
, 也就是常量
反射, 得到Class对象
注: 只有Class.forName()
是主动引用, 类名.class
不是主动引用
被动引用
代码演示:
package com.kuang.reflection;
// 测试类什么时候会初始化
public class Test06 {
static {
System.out.println("main所在类被加载");
}
public static void main(String[] args) throws IllegalAccessException, InstantiationException, ClassNotFoundException {
// 1. new 类
// Son son = new Son();
// 2. 反射
// Class<?> aClass1 = Class.forName("com.kuang.reflection.Son"); // 只是反射, 就已经全部加载
// Class<Son> sonClass = Son.class; // 只加载main
// Class<? extends Son> aClass = son.getClass(); // 全部加载
// aClass.newInstance();
// sonClass.newInstance();
// 不会产生类主动引用的操作
// System.out.println(Son.b); // 只加载main和父
// Son[] arr = new Son[5]; // 只加载main, 说明数组只开辟了空间
System.out.println(Son.M); // 只加载main, 因: 常量池中
/*
测试调用静态成员方法
*/
// System.out.println(Son.m);
/*
结果猜测: 不过顺序不是本例重点
main类被加载
子类被加载
父类被加载
正确答案: 猜测错误!
main
父类
子类
*/
}
}
class Father{
static int b = 2;
static {
System.out.println("父类被加载");
}
}
class Son extends Father{
static {
System.out.println("子类被加载");
m = 300;
}
static int m = 100;
static final int M = 1;
}
类加载器的作用
"类缓存"
类加载到类加载器中, 将维持加载(缓存)一段时间, Class对象会存在一段时间, 就是类缓存. JVM的GC会回收这些Class对象.
JVM规范定义了如下类型的类加载器:
Bootstap Classloader: 引导类加载器, C++编写, 负责Java平台核心库rt.jar, 该类加载器无法直接获取注
无法直接获取, 表现为执行
.getParent()
返回null
Extension Classloader: 扩展类加载器, 负责jre/lib/ext/*.jar
System\Application Classloader: 系统类加载, java -classpath下的.jar, 最常用的加载器
自定义类加载器
补充: rt.jar是什么? java.lang的根目录在哪里?
// 位置:
位于jdk文件, ...jdk1.8.0_71\jre\lib\rt.jar
// 用解压缩软件打开rt.jar:
com, java, javax, jdk, META-INF, org, sun
// 打开java:
applet, awt, io, lang, math, rio, rmi, security, sql, text, time, util
**代码演示: 获取各类加载器 **
/*获取类加载器*/
public class Test07 {
public static void main(String[] args) {
// 获取SystemClassLoader
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
System.out.println(systemClassLoader); // sun.misc.Launcher$AppClassLoader@14dad5dc
// 获取SystemClassLoader的父类加载器: ExtClassLoader
ClassLoader parent = systemClassLoader.getParent();
System.out.println(parent); // sun.misc.Launcher$ExtClassLoader@1b6d3586
// 获取扩展类加载器的父类加载器: 根加载器 (c++)
ClassLoader parent1 = parent.getParent();
System.out.println(parent1); // null, 可能是java语言层面无法理解, 无法直接获取
// 获取当前Test07类是哪个类加载器负责的
Class<?> c1 = Class.forName("com.kuang.reflection.Test07");
ClassLoader classLoader = c1.getClassLoader();
System.out.println(classLoader); // sun.misc.Launcher$AppClassLoader@14dad5dc
// 获取Object类是由哪个类加载器负责加载的
Class<?> c2 = Class.forName("java.lang.Object");
ClassLoader classLoader2 = c2.getClassLoader();
System.out.println(classLoader2); // null
}
}
双亲委派机制
此机制可以确保, 假如手写的包如果和核心类库包路径\名称雷同时, 不会错误地加载自己手写的包, 从而保证了Java核心类库的安全
package com.kuang.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.kuang.reflection.User");
// 获得类的名字
System.out.println(c1.getName()); // 获得完整类名
System.out.println(c1.getSimpleName()); // 获得单纯类名
// 获得类的所有属性
Field[] fields = c1.getFields(); // 只能找到public属性
for (Field field : fields) {
System.out.println(field); // 结果猜错了, 无结果
}
fields = c1.getDeclaredFields(); // 这个才能获取非public的, 更完整的属性们
for (Field field : fields) {
System.out.println(field);
}
/*
private java.lang.String com.kuang.reflection.User.name
private int com.kuang.reflection.User.id
private int com.kuang.reflection.User.age
*/
System.out.println("######################################");
// 获得指定field值
// Field name = c1.getField("name"); // 报错, 找不到这个field, 因为方法找不到private的
// System.out.println(name);
Field name2 = c1.getDeclaredField("name");
System.out.println(name2);
// 获得method
System.out.println("方法: ");
Method[] methods = c1.getMethods(); // 获得本类及父类的全部public方法
for (Method method : methods) {
System.out.println(method);
}
System.out.println("Declared方法"); // 获得本类的所有方法
methods = c1.getDeclaredMethods();
for (Method method : methods) {
System.out.println(method);
}
// 获得指定方法
System.out.println(c1.getMethod("getName", null));
System.out.println(c1.getMethod("setName", String.class));
System.out.println("######################################");
// 获得所有构造器
Constructor<?>[] constructors = c1.getConstructors();
for (Constructor<?> constructor : constructors) {
System.out.println(constructor);
}
constructors = c1.getDeclaredConstructors();
for (Constructor<?> constructor : constructors) {
System.out.println(constructor);
}
System.out.println("######################################");
Constructor<?> declaredConstructor = c1.getDeclaredConstructor(String.class, int.class, int.class);
System.out.println(declaredConstructor);
}
}
User (目标类) 的Class对象调用.newInstance()
, 要求:
// 获得Class对象
Class c1 = Class.forName("com.kuang.reflection.User");
// 构造一个对象
User user = (User)c1.newInstance(); // 本质是调用了类的无参构造
假如不存在无参构造器, 就不能通过上一条途径, 但还可以通过Constructor对象, 传入指定参数构建对象, 步骤如下:
.getDeclaredConstructor(Class... parameterTypes)
, 参数为目标对象参数类型的Class对象String.class
, int.class
...Constructor constructor = c1.getDeclaredConstructor(String.class, int.class, int.class);
User user2 = (User) constructor.newInstance("zhangsan", 1001, 18);
借助Method类完成, 步骤如下:
c1.getDeclaredMethod()\getMethod()
, 参数: 方法名
, 参数类型.class
...method对象.invoke(对象, 具体参数们...)
// 通过反射调用成员方法
User user3 = (User) c1.newInstance();
// 通过反射获取方法
Method setName = c1.getDeclaredMethod("setName", String.class);
setName.invoke(user3, "狂神"); // 调用
// 通过反射操作属性
User user4 = (User) c1.newInstance();
Field name = c1.ge tDeclaredField("name");
name.setAccessible(true); // 操作私有属性之前需要操作
name.set(user4, "李四");
System.out.println(user4); // 报错, 属性是private的, 不能直接操作,需要setAccessible
注意: 如果目标方法\构造器或属性为private
, 则需要提前.setAccessible(true);
setAccessible()
,是"访问安全检查的开关"
- true, 取消访问安全检查, 代码效率也会更高
- false, 实施访问安全检查
package com.kuang.reflection;
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("普通方式执行10亿次:" + (endTime - startTime) + "ms");
}
// 反射方式调用
public static void test02() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
User user = new User();
Class<? extends User> 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("反射1方式执行10亿次:" + (endTime - startTime) + "ms");
}
// 关闭访问安全检测
public static void test03() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
User user = new User();
Class<? extends User> 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("反射2方式执行10亿次:" + (endTime - startTime) + "ms");
}
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
test01();
test02();
test03();
}
}
结果:
普通方式执行10亿次:4ms
反射1方式执行10亿次:2134ms
反射2方式执行10亿次:1266ms
普通方式执行10亿次:5ms
反射1方式执行10亿次:2183ms
反射2方式执行10亿次:1246ms
普通方式执行10亿次:5ms
反射1方式执行10亿次:2087ms
反射2方式执行10亿次:1252ms
代码演示:
package com.kuang.reflection;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Map;
/*通过反射获取泛型*/
public class Test11 {
public void test01 (Map<String, User> map, List<User> list, String string, User user) {
System.out.println("test01");
}
public Map<String, User> test02(){
System.out.println("test02");
return null;
}
public static void main(String[] args) throws NoSuchMethodException {
/*1. 获取方法参数列表中的泛型数据*/
System.out.println("test01()");
Method methodTest01 = Test11.class.getMethod("test01", Map.class, List.class, String.class, User.class);
Type[] genericParameterTypes = methodTest01.getGenericParameterTypes();
for (Type genericParameterType : genericParameterTypes) { // 遍历方法的参数, 还需要取具体泛型
System.out.println(genericParameterType);
if (genericParameterType instanceof ParameterizedType) { // 如果是泛型
Type[] actualTypeArguments = ((ParameterizedType) genericParameterType).getActualTypeArguments();// 就获取真实类型(ActualType)
for (Type actualTypeArgument : actualTypeArguments) {
System.out.println("方法参数列表中的泛型: " + actualTypeArgument);
}
}
}
/*2. 获取方法返回值中的泛型数据*/
System.out.println("test02()");
Method test02Method = Test11.class.getMethod("test02");
Class<?> returnType = test02Method.getReturnType(); // 这种方法不能获取其中的泛型部分
Type genericReturnType = test02Method.getGenericReturnType();
System.out.println(returnType);
System.out.println(genericReturnType);
if (genericReturnType instanceof ParameterizedType) {
Type[] actualTypeArguments = ((ParameterizedType) genericReturnType).getActualTypeArguments();
for (Type actualTypeArgument : actualTypeArguments) {
System.out.println("方法返回值中的泛型类型:" + actualTypeArgument);
}
}
}
}
D:\Software\jdk1.8.0_71\bin\java.exe "-javaagent:D:\Program Files\JetBrains\IntelliJ IDEA 2019.2\lib\idea_rt.jar=64909:D:\Program Files\JetBrains\IntelliJ IDEA 2019.2\bin" -Dfile.encoding=UTF-8 -classpath D:\Software\jdk1.8.0_71\jre\lib\charsets.jar;D:\Software\jdk1.8.0_71\jre\lib\deploy.jar;D:\Software\jdk1.8.0_71\jre\lib\ext\access-bridge-64.jar;D:\Software\jdk1.8.0_71\jre\lib\ext\cldrdata.jar;D:\Software\jdk1.8.0_71\jre\lib\ext\dnsns.jar;D:\Software\jdk1.8.0_71\jre\lib\ext\jaccess.jar;D:\Software\jdk1.8.0_71\jre\lib\ext\jfxrt.jar;D:\Software\jdk1.8.0_71\jre\lib\ext\localedata.jar;D:\Software\jdk1.8.0_71\jre\lib\ext\nashorn.jar;D:\Software\jdk1.8.0_71\jre\lib\ext\sunec.jar;D:\Software\jdk1.8.0_71\jre\lib\ext\sunjce_provider.jar;D:\Software\jdk1.8.0_71\jre\lib\ext\sunmscapi.jar;D:\Software\jdk1.8.0_71\jre\lib\ext\sunpkcs11.jar;D:\Software\jdk1.8.0_71\jre\lib\ext\zipfs.jar;D:\Software\jdk1.8.0_71\jre\lib\javaws.jar;D:\Software\jdk1.8.0_71\jre\lib\jce.jar;D:\Software\jdk1.8.0_71\jre\lib\jfr.jar;D:\Software\jdk1.8.0_71\jre\lib\jfxswt.jar;D:\Software\jdk1.8.0_71\jre\lib\jsse.jar;D:\Software\jdk1.8.0_71\jre\lib\management-agent.jar;D:\Software\jdk1.8.0_71\jre\lib\plugin.jar;D:\Software\jdk1.8.0_71\jre\lib\resources.jar;D:\Software\jdk1.8.0_71\jre\lib\rt.jar;D:\Software-idea\Project-QinJiang\JavaSE\out\production\reflectandnotation com.kuang.reflection.Test11
test01()
java.util.Map<java.lang.String, com.kuang.reflection.User>
方法参数列表中的泛型: class java.lang.String
方法参数列表中的泛型: class com.kuang.reflection.User
java.util.List<com.kuang.reflection.User>
方法参数列表中的泛型: class com.kuang.reflection.User
class java.lang.String
class com.kuang.reflection.User
test02()
interface java.util.Map
java.util.Map<java.lang.String, com.kuang.reflection.User>
方法返回值中的泛型类型:class java.lang.String
方法返回值中的泛型类型:class com.kuang.reflection.User
涉及到的独特而重要的方法:
methodTest01.getGenericParameterTypes()
, 返回方法对象中所有参数的Type[], 包括泛型和泛型参数的genericParameterType instanceof ParameterizedType
, 判断这个Type对象是否属于"泛型Type(参数化类型)"((ParameterizedType) genericParameterType).getActualTypeArguments()
, 抽取Map<String, User>中的String, User的Type对象. 注意需要强转.代码演示:
package com.kuang.reflection;
import java.lang.annotation.*;
import java.lang.reflect.Field;
/*反射获取注解*/
public class Test12 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
/*获取类的Student2类的注解的Class对象*/
Class<?> c1 = Class.forName("com.kuang.reflection.Student2");
Annotation[] annotations = c1.getAnnotations();
for (Annotation annotation : annotations) {
System.out.println(annotation);
}
/*获取注解对象的成员: value*/
Tablekuang tablekuang = c1.getAnnotation(Tablekuang.class);
String value = tablekuang.value();
System.out.println(value);
/*获取类成员的注解*/
Field field = c1.getDeclaredField("name");
Annotation[] annotations1 = field.getAnnotations();
for (Annotation annotation : annotations1) {
System.out.println(annotation);
}
Fieldkuang annotation = field.getAnnotation(Fieldkuang.class);
System.out.println(annotation.columnName());
System.out.println(annotation.type());
System.out.println(annotation.length());
}
}
/*声明类名的注解*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@interface Tablekuang{
String value();
}
/*声明属性的注解*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD})
@interface Fieldkuang{
String columnName();
String type();
int length();
}
/*POJO*/
@Tablekuang("db_student")
class Student2{
@Fieldkuang(columnName="db_name", type="varchar", length = 10)
private String name;
@Fieldkuang(columnName="db_id", type="int", length = 10)
private int id;
@Fieldkuang(columnName="db_age", type="int", length = 10)
private int age;
public Student2() {
}
public Student2(String name, int id, int age) {
this.name = name;
this.id = id;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
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;
}
@Override
public String toString() {
return "Student2{" +
"name=‘" + name + ‘\‘‘ +
", id=" + id +
", age=" + age +
‘}‘;
}
}
运行结果:
D:\Software\jdk1.8.0_71\bin\java.exe "-javaagent:D:\Program Files\JetBrains\IntelliJ IDEA 2019.2\lib\idea_rt.jar=64741:D:\Program Files\JetBrains\IntelliJ IDEA 2019.2\bin" -Dfile.encoding=UTF-8 -classpath D:\Software\jdk1.8.0_71\jre\lib\charsets.jar;D:\Software\jdk1.8.0_71\jre\lib\deploy.jar;D:\Software\jdk1.8.0_71\jre\lib\ext\access-bridge-64.jar;D:\Software\jdk1.8.0_71\jre\lib\ext\cldrdata.jar;D:\Software\jdk1.8.0_71\jre\lib\ext\dnsns.jar;D:\Software\jdk1.8.0_71\jre\lib\ext\jaccess.jar;D:\Software\jdk1.8.0_71\jre\lib\ext\jfxrt.jar;D:\Software\jdk1.8.0_71\jre\lib\ext\localedata.jar;D:\Software\jdk1.8.0_71\jre\lib\ext\nashorn.jar;D:\Software\jdk1.8.0_71\jre\lib\ext\sunec.jar;D:\Software\jdk1.8.0_71\jre\lib\ext\sunjce_provider.jar;D:\Software\jdk1.8.0_71\jre\lib\ext\sunmscapi.jar;D:\Software\jdk1.8.0_71\jre\lib\ext\sunpkcs11.jar;D:\Software\jdk1.8.0_71\jre\lib\ext\zipfs.jar;D:\Software\jdk1.8.0_71\jre\lib\javaws.jar;D:\Software\jdk1.8.0_71\jre\lib\jce.jar;D:\Software\jdk1.8.0_71\jre\lib\jfr.jar;D:\Software\jdk1.8.0_71\jre\lib\jfxswt.jar;D:\Software\jdk1.8.0_71\jre\lib\jsse.jar;D:\Software\jdk1.8.0_71\jre\lib\management-agent.jar;D:\Software\jdk1.8.0_71\jre\lib\plugin.jar;D:\Software\jdk1.8.0_71\jre\lib\resources.jar;D:\Software\jdk1.8.0_71\jre\lib\rt.jar;D:\Software-idea\Project-QinJiang\JavaSE\out\production\reflectandnotation com.kuang.reflection.Test12
@com.kuang.reflection.Tablekuang(value=db_student)
db_student
@com.kuang.reflection.Fieldkuang(columnName=db_name, type=varchar, length=10)
db_name
varchar
10
标签:收集 方法 定义类 run href 使用 类对象 coding 小结
原文地址:https://www.cnblogs.com/clzhang/p/12640830.html