首页 > 编程语言 >Java中什么是反射

Java中什么是反射

时间:2024-08-22 16:26:14浏览次数:13  
标签:反射 Java String 什么 person sql Class name

反射

文章目录

反射概述

反射是什么

反射 (Reflection) 是 Java 的特征之一,它允许运行中的 Java 程序获取自身的信息,并且可以操作类或对象的内部属性。

Oracle 官方对反射的解释是:

Reflection enables Java code to discover information about the fields, methods and constructors of loaded classes, and to use reflected fields, methods, and constructors to operate on their underlying counterparts, within security restrictions.
The API accommodates applications that need access to either the public members of a target object (based on its runtime class) or the members declared by a given class. It also allows programs to suppress default reflective access control.

简而言之,通过反射,我们可以在运行时获得程序或程序中每一个类型的成员和成员的信息。程序中一般的对象的类型都是在编译期就确定下来的,而 Java 反射机制可以动态地创建对象并调用其属性,这样的对象的类型在编译期是未知的。所以我们可以通过反射机制直接创建对象,即使这个对象的类型在编译期是未知的。

反射的核心是 JVM 在运行时才动态加载类或调用方法/访问属性,它不需要事先(写代码的时候或编译期)知道运行对象是谁。

反射的作用

  • 运行时判断任意一个对象所属的类;

  • 运行时创建任意一个类的对象;

  • 运行时判断任意一个类所具有的成员变量和方法(通过反射甚至可以调用private方法);

  • 运行时调用任意一个对象的方法。

    举例:

    Class.forName("com.mysql.jdbc.Driver");注册驱动
    

反射的使用

获取Class

Java的对象模型中:

  1. 所有的类都是Class类的实例,Object是类,那么Object也是Class类的一个实例。

  2. 所有的类都最终继承自Object类,Class是类,那么Class也继承自Object。

首先创建Person类:

package entity;

public class Person {
    private String name;
    public int age;

    public Person() {
    }

    public Person(String name) {
        this.name = name;
    }

    public Person(int age) {
        this.age = age;
    }

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
    
    private void playGame(){
        System.out.println("playing……");
    }

}

获取Class的方法有三种:

  1. 直接获取某个类的class

    Class<Person> personClass = Person.class;
    
  2. 调用某个对象的getClass()方法

    Person person = new Person();
    Class<? extends Person> personClass = person.getClass();
    
  3. 调用Class类的forName()静态方法

    Class<?> personClass = Class.forName("entity.Person");
    

生成实例对象

  1. 调用Class对象的newInstance()方法

    Person person = personClass.newInstance();
    
  2. 通过获取类的构造方法进行实例化

    • 无参构造

      Constructor<Person> constructor = personClass.getConstructor();
      Person person = constructor.newInstance();
      
    • 有参构造

      Constructor<Person> constructor = personClass.getConstructor(String.class, int.class);
      Person person = constructor.newInstance("tom", 18);
      

访问属性

  1. 访问公有属性

    // 获取age属性
    Field ageField = personClass.getField("age");
    // 获取person对象的age属性值
    Object age = ageField.get(person);
    System.out.println(age);
    
    
    //将person对象的age属性值设置为20
    ageField.set(person,20);
    System.out.println(person.age);
    
  2. 访问私有属性

    // 获取私有的name属性
    Field nameField = personClass.getDeclaredField("name");
    // 将name属性的可访问性设置为true
    nameField.setAccessible(true);
    // 获取person对象的name属性值
    Object name = nameField.get(person);
    System.out.println(name);
    
    // 将person对象的name属性值设置为jerry
    nameField.set(person,"jerry");
    System.out.println(person.getName());
    

调用方法

  1. 访问公有方法

    // 获取getName()方法
    Method getName = personClass.getMethod("getName");//getMethod(方法名,参数列表)
    // 通过person对象调用getName()方法,并获取执行结果
    Object name = getName.invoke(person);
    System.out.println(name);
    
    
    // 获取setName()方法
    Method setName = personClass.getMethod("setName", String.class);
    // 通过person对象调用setName()方法,并传递参数
    setName.invoke(person,"trump");
    System.out.println(person.getName());
    
  2. 访问私有方法

    // 获取私有的playGame()方法
    Method playGame = personClass.getDeclaredMethod("playGame");
    // 将playGame()方法的可访问性设置为true
    playGame.setAccessible(true);
    // 通过person对象调用playGame()方法
    playGame.invoke(person);
    

反射的应用

反射封装JDBC框架

传统写法:
 /**
     * 传统jdbc 写法
     *
     * @param args
     * @throws Exception
     */
    public static void main(String[] args) throws Exception {
        //注册驱动
        Class.forName("com.mysql.jdbc.Driver");// 这句话可以省略 驱动自动加载

        Connection connection =
                DriverManager.getConnection("jdbc:mysql:///db01", "root", "root1234");
        // 如果换一张表  student代码就需要改变,下面的赋值 都需要改变 代码写死了 需要灵活
        //虽然我不知道 未来是那个实体类,可以利用反射 在运行时拿到book表对应实体类的信息
        PreparedStatement ps = connection.prepareStatement("insert into student(id,name) values(?,?)");
        ps.setInt(1, 1);
        ps.setString(2, "张三");
        int i = ps.executeUpdate();
        System.out.println(i > 0 ? "成功" : "失败");
        connection.close();
        //把可变的东西 改为灵活的
    }
//问题:每次需要写sql语句 而且sql语句的表名 每次都要换,参数数量不确定。封装一个insert方法

封装AddOne方法

编写dao层代码

public abstract class BaseDao<T> {

   	 public int addOne1(T t) {
        Class<?> aClass = t.getClass();
        String tableName = tClass.getSimpleName().toLowerCase();  //student 数据库表名就拿到了
				
        //"insert into student(id,name) values(?,?)" //
        //2.字符串拼接
        StringBuilder sql = new StringBuilder("insert into ");
        sql.append(tableName);
        sql.append("(");

       //3.获取属性
        Field[] fields = aClass.getDeclaredFields();
        for (Field field : fields) {
            String fieldName = field.getName();
            sql.append(fieldName);
            sql.append(",");//最后多了一个逗号
        }

        sql.deleteCharAt(sql.length() - 1);//删除最后一位字符
        sql.append(") values (");

        for (Field field : fields) {
            sql.append("?,");//最后多了一个逗号
        }
        sql.deleteCharAt(sql.length() - 1);//删除最后一位字符
        sql.append(")");

        System.out.println(sql);//insert into student(id,name) values (?,?)

        //补全 ? ?
       Connection connection = null;
        try {
            connection = DriverManager.getConnection("jdbc:mysql:///db01", "root", "root1234");
            PreparedStatement ps = connection.prepareStatement(sql.toString());
            //补全几个? ?  是位置的  需要循环
            for (int i = 0; i < fields.length; i++) {
                Field field = fields[i];
                field.setAccessible(true);//允许访问私有属性
                Object value = field.get(t);
                ps.setObject(i + 1, value);
            }

            return ps.executeUpdate();//执行sql

        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            if (connection != null) {
                try {
                    connection.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
        return 0;
    }
}
    /**
     * 测试类
     */
    @Test
    public void test2() {
        StudentDao studentDao = new StudentDao();
        Student student = new Student();
        student.setId(null);//自动增长
        student.setName("王武");
        studentDao.addOne(student);

        //防止new BaseDao()  设置成抽象类
        //BaseDao<Student> dao = new BaseDao<Student>();
    }
封装find方法
封装updateById方法
封装deleteById方法
Guava
<dependency>
  <groupId>com.google.guava</groupId>
  <artifactId>guava</artifactId>
  <version>31.1-jre</version>
</dependency>
				//驼峰转下划线
        String tableName = "PersonDog";
        String to = CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, tableName);
        System.out.println(to);//person_dog

        //下划线转驼峰
        String tableName = "person_dog";
        String to = CaseFormat.LOWER_UNDERSCORE.to(CaseFormat.UPPER_CAMEL, tableName);
        System.out.println(to);//PersonDog

自定义注解

package com.codingfuture.annotation;

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Id {
    String value = "";
}


@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface TableName {
    String value();
}
  • Target

    • FIELD:表示"被标注的注解"只能出现在属性上。
    • TYPE:表示"被标注的注解"只能出现在类上。
    • METHOD:表示"被标注的注解"只能出现在方法上。
    • PARAMETER:表示"被标注的注解"只能出现在参数上。
    • CONSTRUCTOR:表示"被标注的注解"只能出现在构造上。
  • Retention

    • 这个Retention注解用来标注"被标注的注解"最终保存在哪里。
      @Retention(RetentionPolicy.SOURCE):表示该注解只能被保留在java源文件中。
      @Retention(RetentionPolicy.CLASS):表示该注解被保存在class文件中。
      @Retention(RetentionPolicy.RUNTIME):表示该注解被保存在class文件中,并且可以被反射机制所读取。
  • 获取指定类型是否加参数

    • field.isAnnotationPresent(Id.class)
      
    • String tableName = this.aClass.getSimpleName();
      if (aClass.isAnnotationPresent(TableName.class)) {
            TableName declaredAnnotation = aClass.getDeclaredAnnotation(TableName.class);
            tableName = declaredAnnotation.value();
      }
      

标签:反射,Java,String,什么,person,sql,Class,name
From: https://blog.csdn.net/venus_patton/article/details/141431110

相关文章

  • Java之日期
    目录日期java.time的API(目前多用time)一、LocalDate1.基本用法2.日期之间的转化3.LocalDateTime计算4.格式化日期二、Instant1.基本应用2.常见方法3.时间计算4.时间转化三、DateTimeFormatter时间格式化1.基本用法2.各类时间格式化四、ZonedDateTime1.基本用法2.Zo......
  • java+vue计算机毕设旅游景点预约系统【源码+开题+论文】
    本系统(程序+源码)带文档lw万字以上文末可获取一份本项目的java源码和数据库参考。系统程序文件列表开题报告内容研究背景随着旅游业的蓬勃发展,人们对旅游体验的需求日益个性化与高效化。传统的旅游预订方式往往存在信息不对称、购票流程繁琐、景点拥堵等问题,影响了游客的......
  • java+vue计算机毕设开放实验室网上预约系统【源码+开题+论文】
    本系统(程序+源码)带文档lw万字以上文末可获取一份本项目的java源码和数据库参考。系统程序文件列表开题报告内容研究背景随着高等教育体系的不断发展和教育资源的日益丰富,实验室作为培养学生实践能力和创新精神的重要场所,其使用效率与管理水平成为衡量高校教学质量的重要......
  • java+vue计算机毕设农资电子监管系统的设计与实现【源码+开题+论文】
    本系统(程序+源码)带文档lw万字以上文末可获取一份本项目的java源码和数据库参考。系统程序文件列表开题报告内容研究背景随着农业现代化的不断推进,农资产品的流通与管理成为保障农业生产高效、安全的重要环节。传统农资管理模式存在信息不对称、监管难度大、效率低下等问......
  • java版本12计算2000年1月到2023年6月相差几年
     JDK12版本importjava.time.YearMonth;importjava.time.temporal.ChronoUnit;publicclassYearsBetweenDates{publicstaticvoidmain(String[]args){YearMonthstartYearMonth=YearMonth.of(2000,1);YearMonthendYearMonth=YearMon......
  • Java学习笔记4
    1、应用范畴不同:主键属于索引的一种。在数据库关系图中为表定义主键将自动创建主键索引,主键索引是唯一索引的特定类型。该索引要求主键中的每个值都唯一。当在查询中使用主键索引时,它还允许对数据的快速访问。2、种类不同:根据数据库的功能,可以在数据库设计器中创建三种索引:唯一索......
  • Java 之反射
    目录反射一、通过反射获取class的四种方法二、反射中访问字段三、获取反射的方法四、反射调用构造方法五、反射获取继承关系反射概念:通过Classs实例获取class信息的方法称为反射一、通过反射获取class的四种方法/***通过反射获取class实例的方法有四种:*/......
  • 【HTML】使用Javascript制作网页
    1、Javascript的语法规则JavaScript程序按照在HTML文件中出现的顺序逐行执行。JavaScript严格区分字母大小写。在JavaScript中,每行结尾的分号可有可无。JavaScript中主要包括两种注释:单行注释和多行注释。单行注释使用双斜线“//”作为注释标签,多行注释是以“/”标签开始,以......
  • C#通过反射拿到枚举,但是里面有个“value__”,怎么办?
    1usingSystem;2usingUnityEngine;3usingSystem.Reflection;4publicenumExampleEnum5{6Value1,7Value28}9classProgram:MonoBehaviour10{11voidStart()12{13TypeenumType=typeof(ExampleEnum);14......
  • 常见问题解决 --- 为什么我们常常发现服务器没有管理的端口
    我们在扫描一台主机全端口,发现没有开放管理端口,比如windows远程桌面或者是linux的ssh登陆。我列举一下常见的原因。常规管理方式:1.管理口不是常见的3389和22端口,而改为了高位端口号,避免被人发现。2.在管理端口上加上了安全策略导致无法直接连接,比如私钥登陆方......