Java学习笔记40

news/2024/11/17 1:27:56/

Java笔记40

创建运行时类的对象

获取运行时类的完整结构

  • 我们可以通过反射来获取运行时类的完整结构,这包括:
    • 实现的全部接口(Interface)
    • 所继承的父类(Superclass)
    • 全部的构造器(Constructor)
    • 全部的方法(Method)
    • 全部的属性/字段(Field)
    • 注解(Annotation)
    • ……
  • 下面我们写一段代码来练习一下获取类的信息:
  • 首先我们创建一个类:

Dog.java

package com.clown.reflection;public class Dog {//属性public String name;private int age;private String ownerName;//无参构造public Dog() {}//有参构造public Dog(String name, int age, String ownerName) {this.name = name;this.age = age;this.ownerName = ownerName;}//get() & set()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;}public String getOwnerName() {return ownerName;}public void setOwnerName(String ownerName) {this.ownerName = ownerName;}//重写 toString()@Overridepublic String toString() {return "Dog{" +"name='" + name + '\'' +", age=" + age +", ownerName='" + ownerName + '\'' +'}';}public void run() {System.out.println(this.getName() + "正在奔跑");}protected void eat() {System.out.println(this.getName() + "正在吃东西");}private void pee() {System.out.println(this.getName() + "正在尿尿");}
}
  • 然后我们来尝试获取Dog类中的信息:

Test08.java

package com.clown.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 Exception {Class c1 = Class.forName("com.clown.reflection.Dog");//获得类的名字System.out.println("=============================== 获得类的名字 ===============================");System.out.println("包名+类名: " + c1.getName());  //getName();  获得包名 + 类名System.out.println("--------------------------------------------------------------");System.out.println("类名: " + c1.getSimpleName());  //getSimpleName();  获得类名//获得类的属性System.out.println("=============================== 获得类的属性 ===============================");Field[] fields = c1.getFields();  //getFields();  获得本类的所有 public属性for (Field field : fields) {System.out.println("public属性: " + field);}System.out.println("--------------------------------------------------------------");fields = c1.getDeclaredFields();  //getDeclaredFields();  获得本类的所有属性for (Field field : fields) {System.out.println("所有属性: " + field);}System.out.println("--------------------------------------------------------------");Field name = c1.getDeclaredField("age");  //getDeclaredField(String name);  获得本类中指定的属性System.out.println("指定属性: " + name);//获得类的方法System.out.println("=============================== 获得类的方法 ===============================");Method[] methods = c1.getMethods();  //getMethods();  获得本类及其父类的所有 public方法for (Method method : methods) {System.out.println("public方法: " + method);}System.out.println("--------------------------------------------------------------");methods = c1.getDeclaredMethods();  //getDeclaredMethods();  获得本类的所有方法for (Method method : methods) {System.out.println("所有方法: " + method);}System.out.println("--------------------------------------------------------------");Method getName = c1.getMethod("getName", null);  //getMethod("XXX", XXX);  获得本类中指定的方法System.out.println("指定方法: " + getName);Method setName = c1.getMethod("setName", String.class);System.out.println("指定方法: " + setName);/*getMethod(String name, Class<?>... parameterTypes);  获得本类中指定的方法参数:name参数是一个 String,它指定了所需方法的简单名称。parameterTypes参数是以声明顺序标识方法的形式参数类型的类对象的数组。如果 parameterTypes是 null ,它被视为一个空数组。*///获得类的构造器System.out.println("============================== 获得类的构造器 ==============================");Constructor[] constructors = c1.getConstructors();  //getConstructors();  获得本类的 public构造器for (Constructor constructor : constructors) {System.out.println("public构造器: " + constructor);}System.out.println("--------------------------------------------------------------");constructors = c1.getDeclaredConstructors();  //getDeclaredConstructors();  获得本类的所有构造器for (Constructor constructor : constructors) {System.out.println("所有构造器: " + constructor);}System.out.println("--------------------------------------------------------------");Constructor constructor = c1.getConstructor(String.class, int.class, String.class);  //getConstructor(xxx)  获得本类中指定的构造器System.out.println("指定构造器: " + constructor);/*getConstructor(Class<?>... parameterTypes);  获得本类中指定的构造器参数:parameterTypes参数是以声明顺序标识构造函数的形式参数类型的类对象的数组*/}
}
  • 运行结果:

在这里插入图片描述

获取了Class对象,通过反射我们能做什么?

一、反射创建类的对象

  • 调用Class对象的newlnstance()方法

    • (1)类必须有一个无参数的构造器。
    • (2)类的构造器的访问权限需要足够
  • 思考:难道没有无参的构造器就不能创建对象了吗?

  • 只要在操作的时候明确的调用类中的构造器,调用构造器对象的newInstance(Object ... initargs)方法,并将参数传递进去,就可以实例化对象了。具体步骤如下:

    • (1)通过Class类的getConstructor(Class<?>... parameterTypes)取得本类的指定形参类型的构造器
    • (2)向构造器的形参中传递一个对象数组进去,里面包含了构造器中所需的各个参数。
    • (3)通过构造器(Constructor)实例化对象

二、反射调用类中的方法

  • 通过Method类完成。

    • 通过Class类的getMethod(String name, Class<?>... parameterTypes)方法取得一个Method对象,并设置此方法操作时所需要的参数类型。
    • 之后使用public Object invoke(Object obj, Object... args)方法进行调用,并向方法中传递要设置的obj对象的参数信息。

在这里插入图片描述

  • Object invoke(Object obj, Object … args)

    • Object对应原方法的返回值,若原方法无返回值,此时返回null
    • 若原方法为静态方法,此时形参Object obj可为null
    • 若原方法形参列表为空,则Object[] argsnull
    • 若原方法声明为private,则需要在调用此invoke()方法前,显式调用方法对象的setAccessible(true)方法,将可访问private的方法。
  • setAccessible(boolean flag)

    • MethodFiedConstructor对象都有setAccessible()方法。
    • setAccessible()的作用是启动和禁用访问安全检查的开关。
    • 参数值为true则指示反射的对象在使用时应该取消 Java 语言访问检查。
      • 提高反射的效率。如果代码中必须用反射,而该句代码需要频繁的被调用,那么请设置为true
      • 使得原本无法访问的私有成员也可以访问。
    • 参数值为false则指示反射的对象应该实施 Java 语言访问检查。

三、反射操作类中的属性

  • 通过Field类完成。

    • 通过Class类的getDeclaredField(String name)方法取得一个Field对象。
    • 之后使用public void set(Object obj, Object value)方法将obj对象参数上的此Field对象表示的属性设置为指定的新值。
  • 下面我们来写一段程序来测试一下通过反射创建类的对象、调用类中的方法以及操作类中的属性。我们使用上面创建的Dog类创建Class对象:

package com.clown.reflection;import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;public class Test09 {public static void main(String[] args) throws Exception {//获得 Class对象Class c1 = Class.forName("com.clown.reflection.Dog");//通过反射,动态的创建对象//通过调用 Class对象的 newInstance()创建对象System.out.println("================== 通过Class对象的newInstance()方法创建对象 ==================");//newInstance();  创建由此类对象表示的类的新实例
//        Object obj = c1.newInstance();
//        Dog dog1 = (Dog)obj;Dog dog1 = (Dog)c1.newInstance();  //本质上是调用了 Dog类的无参构造器System.out.println(dog1);//通过构造器对象的 newInstance(Object ... initargs)方法创建对象System.out.println("=============== 通过构造器对象的newInstance(参数值)方法创建对象 ===============");Constructor constructor = c1.getDeclaredConstructor(String.class, int.class, String.class);//newInstance(Object ... initargs);  使用由此 Constructor对象表示的构造函数,用指定的初始化参数创建和初始化构造函数的声明类的新实例Dog dog2 = (Dog)constructor.newInstance("旺财", 5, "张三");System.out.println(dog2);//通过反射调用普通方法System.out.println("============================= 通过反射调用方法 ==============================");Dog dog3 = (Dog)c1.newInstance();Method setName = c1.getDeclaredMethod("setName", String.class);//invoke(Object obj, Object... args);  在具有指定参数的 Method对象上调用此 Method对象表示的基础方法。//参数: obj - 从底层方法被调用的对象    args - 用于方法调用的参数值setName.invoke(dog3, "大黄");System.out.println(dog3.getName());Method pee = c1.getDeclaredMethod("pee", null);//注意: 不能直接操作私有(private)的方法或属性,我们需要先关闭程序的安全检测 - setAccessible(true)//setAccessible(boolean flag);  将此对象的 accessible标志设置为指定的布尔值//当值为 true时表示反射对象应该在使用时抑制程序的的安全检查。 值为 false时表示反射的对象应该强制执行程序的的安全检查。pee.setAccessible(true);pee.invoke(dog3, null);//通过反射操作属性System.out.println("============================= 通过反射操作属性 ==============================");Dog dog4 = (Dog)c1.newInstance();Field age = c1.getDeclaredField("age");age.setAccessible(true);  //关闭程序的安全检测//set(Object obj, Object value);  将指定对象参数上的此 Field对象表示的字段设置为指定的新值age.set(dog4, 8);System.out.println(dog4.getAge());}
}
  • 运行结果:

在这里插入图片描述

使用不同方式调用方法的性能测试

  • 在上面我们说过使用setAccessible(boolean flag)方法关闭程序的检测能提高反射的效率,那么是否真是如此呢?又大概提高了多少呢?下面我们就写一段程序来测试一下使用不同的方式调用方法对性能的影响:
package com.clown.reflection;import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;//测试性能
public class Test10 {public static void main(String[] args) throws InvocationTargetException, NoSuchMethodException, IllegalAccessException {test01();test02();test03();}//普通方式调用public static void test01() {Dog dog = new Dog();long startTime = System.currentTimeMillis();for (int i = 0; i < 1000000000; i++) {dog.getName();}long endTime = System.currentTimeMillis();System.out.println("普通方式执行10亿次的时间: " + (endTime - startTime) + "毫秒");}//反射方式调用public static void test02() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {Dog dog = new Dog();//获取 Class对象Class c1 = dog.getClass();//获取 Method对象Method getName = c1.getDeclaredMethod("getName", null);long startTime = System.currentTimeMillis();for (int i = 0; i < 1000000000; i++) {getName.invoke(dog, null);  //调用方法}long endTime = System.currentTimeMillis();System.out.println("反射方式执行10亿次的时间: " + (endTime - startTime) + "毫秒");}//反射方式调用,关闭检测public static void test03() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {Dog dog = new Dog();//获取 Class对象Class c1 = dog.getClass();//获取 Method对象Method getName = c1.getDeclaredMethod("getName", null);getName.setAccessible(true);  //关闭检测long startTime = System.currentTimeMillis();for (int i = 0; i < 1000000000; i++) {getName.invoke(dog, null);  //调用方法}long endTime = System.currentTimeMillis();System.out.println("反射方式并关闭检测执行10亿次的时间: " + (endTime - startTime) + "毫秒");}
}
  • 运行结果:

在这里插入图片描述

四、拓展:反射操作泛型

  • Java 采用泛型擦除的机制来引入泛型,Java 中的泛型仅仅是给编译器 Javac 使用的,确保数据的安全性和免去强制类型转换问题,但是,一旦编译完成,所有和泛型有关的类型全部擦除。
  • 为了通过反射操作这些类型,Java新增了ParameterizedTypeGenericArrayTypeTypeVariableWildcardType几种类型来代表不能被归一到Class类中的类型但是又和原始类型齐名的类型。
    • ParameterizedType:表示一种参数化类型,比如Collection<String>
    • GenericArrayType:表示一种元素类型是参数化类型或者类型变量的数组类型。
    • TypeVariable:是各种类型变量的公共父接口。
    • WildcardType:代表一种通配符类型表达式。
  • 下面我们编写代码来练习一下如何通过反射来获取泛型:
package com.clown.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 static void main(String[] args) throws NoSuchMethodException {System.out.println("=========================== 获取方法test01()的形式参数类型 ============================");Method method1 = Test11.class.getDeclaredMethod("test01", Map.class, List.class);//getGenericParameterTypes();  返回一个 Type对象的数组,Type[]以声明顺序表示由该 Method对象表示的方法的形式参数类型Type[] genericParameterTypes = method1.getGenericParameterTypes();for (Type genericParameterType : genericParameterTypes) {System.out.println("test01()方法的形式参数的类型: " + genericParameterType);//ParameterizedType接口: 表示一个泛型,如 Collection <String>if (genericParameterType instanceof ParameterizedType) {  //判断该形式参数是否为泛型//getActualTypeArguments();  返回一个表示此类型的实际类型参数的数组 Type对象Type[] actualTypeArguments = ((ParameterizedType) genericParameterType).getActualTypeArguments();for (Type actualTypeArgument : actualTypeArguments) {System.out.println("此泛型的实际类型参数类型: " + actualTypeArgument);}}}System.out.println("============================ 获取方法test02()的返回值类型 ============================");Method method2 = Test11.class.getDeclaredMethod("test02", null);//getGenericReturnType();  返回一个 Type对象,它表示由该 Method对象表示的方法的正式返回类型Type genericReturnType = method2.getGenericReturnType();System.out.println("test02()方法的返回值的类型: " + genericReturnType);if (genericReturnType instanceof ParameterizedType) {Type[] actualTypeArguments = ((ParameterizedType) genericReturnType).getActualTypeArguments();for (Type actualTypeArgument : actualTypeArguments) {System.out.println("此泛型的实际类型参数类型: " + actualTypeArgument);}}}//定义一个方法,它的形式参数为泛型public void test01(Map<String, Dog> map, List<Dog> list) {System.out.println("test01");}//定义一个方法,它的返回值类型类型为泛型public Map<String, Dog> test02() {System.out.println("test02");return null;}}
  • 运行结果:

在这里插入图片描述

了解什么是ORM

  • Object Relationship Mapping --> 对象关系映射
    在这里插入图片描述
    • 类和表结构对应
    • 属性和字段对应
    • 对象和记录对应
  • 要求:利用注解和反射完成类和表结构的映射关系

五、反射操作注解

  • 我们知道通过Class类的getAnnotations()方法可以获取该类的所有注解。
  • 但是怎样才能获取类中指定的注解呢?
  • 例如:我们想获得类中某个属性(Filed)的注解,那我们只需要先获得该属性的Filed对象,然后再使用该Filed对象的getAnnotation(Class<A> annotationClass)方法,并传入我们想要获取的注解的Class对象,即可获得该注解。同理,若我们想获得某个类(Class)/方法(Method)等的指定的注解也是使用getAnnotation(Class<A> annotationClass)方法。
package com.clown.reflection;import java.lang.annotation.*;
import java.lang.reflect.Field;//通过反射操作注解
//ORM
public class Test12 {public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {//获得 Class对象Class c1 = Class.forName("com.clown.reflection.Student2");//通过反射获得注解Annotation[] annotations = c1.getAnnotations();for (Annotation annotation : annotations) {System.out.println(annotation);}//获得注解的 value的值TableClown tableClown = (TableClown) c1.getAnnotation(TableClown.class);String value = tableClown.value();System.out.println(value);System.out.println("===================================================");//获得类中的指定的注解Field f = c1.getDeclaredField("id");FiledClown filedClown = f.getAnnotation(FiledClown.class);System.out.println(filedClown);//获取并打印注解的参数的值System.out.println(filedClown.columnName());System.out.println(filedClown.type());System.out.println(filedClown.length());}}//创建一个实体类 -->  学生类
@TableClown("db_student")
class Student2 {//属性@FiledClown(columnName = "bd_id", type = "int", length = 10)private int id;@FiledClown(columnName = "bd_age", type = "int", length = 3)private int age;@FiledClown(columnName = "bd_name", type = "varchar", length = 4)  //在数据库中,"String" 一般用 "varchar" 表示private String name;//无参构造public Student2() {}//有参构造public Student2(int id, int age, String name) {this.id = id;this.age = age;this.name = name;}//get() & set()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;}//重写 toString()@Overridepublic String toString() {return "Student2{" +"id=" + id +", age=" + age +", name='" + name + '\'' +'}';}
}//自定义创建一个【类】的注解
@Target(ElementType.TYPE)  //@TableClown可以放在【类】上
@Retention(RetentionPolicy.RUNTIME)  //@TableClown在源码时、编译为 class时以及运行时都有效
@interface TableClown {String value();  //参数: 数据库名
}//自定义创建一个【属性】的注解
@Target(ElementType.FIELD)  //@FiledClown可以放在【属性】上
@Retention(RetentionPolicy.RUNTIME)  //@FiledClown在源码时、编译为 class时以及运行时都有效
@interface FiledClown {String columnName();  //参数: 列名String type();  //参数: 类型int length();  //参数: 长度
}
  • 运行结果:

在这里插入图片描述


http://www.ppmy.cn/news/1055725.html

相关文章

rust写一个多线程和协程的例子

当涉及到多线程和协程时&#xff0c;Rust提供了一些非常强大的工具&#xff0c;其中最常用的库之一是tokio&#xff0c;它用于异步编程和协程。下面我将为你展示一个简单的Rust程序&#xff0c;演示如何使用多线程和协程。 首先&#xff0c;你需要在你的项目的Cargo.toml文件中…

【PHP】PHP的面向对象编程

PHP面向对象编程是PHP编程的一个重要方向&#xff0c;它通过将数据和操作封装在对象中&#xff0c;使得代码更加模块化、可重用性和易于维护。本文将介绍PHP面向对象编程的基本概念、核心思想和常见应用&#xff0c;并探讨使用PHP面向对象编程时需要注意的问题。 一、概述 PH…

软考高级系统架构设计师系列论文七十六:论基于构件的软件开发

软考高级系统架构设计师系列论文七十六:论基于构件的软件开发 一、构件相关知识点二、摘要三、正文四、总结一、构件相关知识点 软考高级系统架构设计师系列之:面向构件的软件设计,构件平台与典型架构

如何使用CSS实现一个响应式轮播图?

聚沙成塔每天进步一点点 ⭐ 专栏简介⭐ 使用CSS实现响应式轮播图的示例⭐ HTML 结构⭐ CSS 样式 (styles.css)⭐ JavaScript 代码 (script.js)⭐ 实现说明⭐ 写在最后 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 记得点击上方或者右侧链接订阅本专栏哦 几何带…

PDA手持终端联发科安卓主板方案定制_4G5G通讯模块PDA方案开发

PDA手持终端是一种便携式电子设备&#xff0c;具备计算、通讯、存储和数据处理等多种功能&#xff0c;广泛应用于商业、工业、医疗、物流和日常生活等领域。 手持终端pda作为一种快速手持数据处理设备&#xff0c;通常集成了与数据采集和传输密切相关的功能&#xff0c;如条码…

多种方式让你快速访问 GitHub

平时工作中&#xff0c;你可能和笔者一样会遇到访问 GitHub 缓慢或者打不开的问题。这里我总结了一些方法或许可以帮助你快速访问 GitHub&#xff0c;下载大项目还是获取小型代码和文档项目都有不同方案。 一&#xff1a;使用镜像网站 如果需要下载大型项目&#xff0c;特别是下…

向量检索:基于ResNet预训练模型构建以图搜图系统

1 项目背景介绍 以图搜图是一种向量检索技术&#xff0c;通过上传一张图像来搜索并找到与之相关的其他图像或相关信息。以图搜图技术提供了一种更直观、更高效的信息检索方式。这种技术应用场景和价值非常广泛&#xff0c;经常会用在商品检索及购物、动植物识别、食品识别、知…

.NET 8 Preview 7 中的 ASP.NET Core 更新

作者&#xff1a;Daniel Roth 排版&#xff1a;Alan Wang .NET 8 Preview 7 现在已经发布&#xff0c;其中包括了对 ASP.NET Core 的许多重要更新。 以下是预览版本中新增功能的摘要&#xff1a; 服务器和中间件 防伪中间件 API 编写 最小 API 的防伪集成 Native AOT 请求委托…