对象克隆、反射、注解

本文介绍了Java中对象的浅克隆与深克隆的区别,以及如何使用反射机制获取类的信息,并通过反射创建对象和调用方法。同时,还探讨了反射在实际应用中的灵活性优势及注解的基本用法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

对象的克隆

对象的浅克隆:

对象的深克隆:

反射

基本概念说明:

使用反射机制的步骤:

反射泛型

反射有什么用

注解

 


对象的克隆


对象的浅克隆:

对象1  实例1 = (对象2)实例2.clone();


注意细节:

  1. 如果一个对象需要调用clone的方法克隆,那么该对象所属的类必须要实现Cloneable接口。  对象2要实现Cloneable接口
  2. Cloneable接口只不过是一个标识接口而已,没有任何方法。
  3. 对象的浅克隆就是克隆一个对象的时候,如果被克隆的对象中维护了其他类的对象,这时候只是克隆其他对象的地址,而没有把其他对象也克隆一份。 如果对象2中引用了对象3,对象1克隆对象2后,只是指向对象3而不会克隆对象3
  4. 对象的浅克隆不会调用到构造方法的。

对象的深克隆:

对象的深克隆就是利用对象序列化的输入输出流把对象先写到文件上,然后再读取对象的信息这个过程就称作为对象的深克隆。
ObjectInputStream
ObjectOutputStream

先使对象实现Serializable接口,然后把对象(实际上只是对象的一个拷贝)写到一个流里(腌成咸菜),再从流里读出来(把咸菜回鲜),便可以重建对象。

 

内存溢出:不再使用的对象,应该不要让变量指向该对象,要让该对象尽快的被垃圾回收期回收。


反射

基本概念说明:

1.类加载器(class loader)用来加载 Java 类到 Java 虚拟机中。

一般来说,Java 虚拟机使用 Java 类的方式如下:

Java 源程序(.java 文件)---Java 编译器编译--> Java 字节代码(.class 文件)---类加载器负责读取并转换--> java.lang.Class 类的一个实例。(一个类只有一个Class对象)

每个这样的实例用来表示一个 Java 类。通过此实例的 newInstance()方法就可以创建出该类的一个对象。

 

 是个模板,有属性,方法

类实例化对象 属性有具体的值,方法有具体的动作

类对象 用于描述这种类,都有什么属性,什么方法的

2.类对象(class对象):描述类的对象。所有的类都存在一个类对象,这个类对象用于提供类本身的信息,如构造方法,属性,普通方法。

字节码的信息包括:类名、声明的方法、声明的字段、构造方法等信息,封装在Class类、Method类、Field类、Constructor类中。一个类的任何成员都有对应的类进行描述

我们只要获取到Class对象,我们就可以通过Class类、Method类、Field类、Constructor类等等类得到这个类型的一些信息,甚至可以不用new关键字就创建一个实例,执行一个对象中的方法,设置或获取字段的值,这就是反射技术

可以在运行时期动态创建对象;获取对象的属性、方法

正常方式: 引入需要的“包.类”名称 --> 通过new实例化 --> 取得实例化对象

反射方式: 实例化对象 --> getClass()方法 --> 得到完整的“包.类”名称

所有类的对象实际上都是Class类的实例。在Java中Object类是一切类的父类,那么所有类的对象实际上也就是java.lang.Class类的实例,所以所有的对象都可以转变为lang.lang.Class类型表示

反射机制中的类

java.lang.Class;         Java反射机制的起源

java.lang.reflect.Constructor;      //构造方法

java.lang.reflect.Field;      //成员变量

java.lang.reflect.Method;     //方法

java.lang.reflect.Modifier;     //修饰符


使用反射机制的步骤:

1.导入java.lang.relfect包
2.遵循三个步骤

第一步是获得你想操作的类的 java.lang.Class 对象

第二步是调用诸如 getDeclaredMethods 的方法

第三步使用 反射API 来操作这些信息

第一步: 获取Class对象的三种方式:

  1. Class.forName("完整类名")       forName方法用于加载某个类的字节码到内存中,并使用class对象进行封装。类名在编译期不知道但是在运行期可以获得 推荐使用
  2. 类名.class           在编译期知道类的名字可以使用此方法
  3. new 对象().getClass()           已经得到一个类的实例可以使用此方法

无论什么途径获取类对象,都会导致静态属性被初始化,而且只会执行一次。(除了直接使用 Class c = Hero.class 这种方式,这种方式不会导致静态属性被初始化)

第二步: 

1.获取类对象

通过类对象获取“构造器对象” , 再通过构造器对象创建一个对象

//类对象
Class hero=Class.forName(className);
//构造器
Constructor c= hero.getConstructor();
//通过构造器实例化
Hero h2= (Hero) c.newInstance();
h2.name="gareen";
System.out.println(h2);

 

反射相关api:

newInstance()        用反射的方式创建一个对象的实例,相当于new

getName()     类的名称(全名,全限定名)
getSimpleName()     类的的简单名称(不带包名)
getModifiers();       类的的修饰符

getConstructors()      获取一个类的所有公共的构造方法
getDeclaredConstructors();      获取到一个类的所有构造方法,包括私有
getConstructor(int.class,String.class);       获取单个指定的构造方法

getMethods()     获取公共方法包括继承的父类的方法,返回一个数组,元素类型是Method
getMethod("setName", String.class);      获取指定参数的公共方法
Method[] getDeclaredMethods()      获得所有的方法,包括私有
Method getDeclaredMethod(String name, Class<?>... parameterTypes)     获得指定参数的方法,包括私有

Field[] getFields()      获取公共成员变量
Field getField(String name)       获取指定参数的公共成员变量
Field[] getDeclaredFields()       获取所有的成员变量
Field getDeclaredField(String name)      获取指定参数的成员变量,包括私有


反射泛型


Type接口         所有类型的默认接口    包括: 引用类型、原始类型、参数化类型···
参数化类型 ParameterizedType:   例  “ArrayList<String>”为参数化类型,“String”为实际的类型

案例

设计一个BaseDao类,所有dao的公用的方法都在里面实现。每个对象的dao继承BaseDao,通过反射泛型将具体的类型给BaseDao

public class AdminDao extends BaseDao<Admin> {}
public class AccountDao extends BaseDao<account> {}

public class BaseDao<T>{
	// 参数化类型中的实际的类型
	private Class clazz;
	// 表名
	private String tableName;

    // 构造函数: 1. 获取当前运行类的参数化类型; 2. 获取参数化类型中实际类型的定义(class)
    public BaseDao(){
	// this.getClass().getGenericSuperclass(); 当前运行类的父类【BaseDao<Admin>】就是“参数化类型”
	Type type = this.getClass().getGenericSuperclass();
	// 强制转换为“参数化类型” 【BaseDao<Admin>】
	ParameterizedType pt = (ParameterizedType) type;
	// 获取参数化类型中,实际类型的定义 【new Type[]{Admin.class}】
	Type types[] = pt.getActualTypeArguments();
	// 获取数据的第一个元素【Admin.class】
	clazz = (Class) types[0];
	// 表名 (与类名一样,只要获取类名就可以)
	tableName = clazz.getSimpleName();
    }
    //获取实际类型class 与表名tableName后,根据id查询
    public T findById(int id){
	String sql = "select * from " + tableName + " where id=? ";
	try {
		return JdbcUtils.getQuerrRunner().query(sql, new BeanHandler(clazz), id);
	} catch (SQLException e) {
		throw new RuntimeException(e);
	}
    }
}
(clazz), id);
	} catch (SQLException e) {
		throw new RuntimeException(e);
	}
    }
}

 

反射有什么用

反射调用类方法不如直接调用方法来的直接和方便, 但是反射类更加灵活

例如 要在程序中把 类1 换成 类2,

正常情况只能修改代码,重新编译,然后部署

利用反射, 可以先准备一个xml配置文件,文件写上类名称与方法, 然后用程序去读取配置文件,将读取到的类名通过反射获取到。

这样只要改配置文件就能控制程序调用的类与方法

这也是Spring框架的最基本的原理

 


注解

注解与注释

注解:告诉编译器如何运行程序!

注释:给程序员阅读,对编译、运行没有影响

注解作用

1. 告诉编译器如何运行程序

2. 简化(取代)配置文件

常用注解

@Override     重写父类的方法

@SuppressWarnings     忽略警告信息

@Deprecated     标记方法已经过时

自定义注解

不使用class也不使用interface,而是使用@interface

例:定义一个作者注解

public @interface Author {
	/**注解属性
	 * 1. 修饰符为默认或public
	 * 2. 不能有主体
	 */ 
	String authorName();
	int age() default 30; // 带默认值的注解; 使用的时候就可以不写此属性值
	String remark();
}
//使用时要给每个属性名称赋值。【注意:只有注解一个属性,且名称为value时候,可以省略名称直接赋值】
@Author(authorName = "Jet")

元注解:表示注解的注解!指定注解的可用范围

@Target:指定注解的作用范围
  @Target({TYPE//类    FIELD//字段   METHOD//方法   PARAMETER//参数   CONSTRUCTOR//构造器 LOCAL_VARIABLE//局部变量})

@Retention:指定注解的生命周期
  @Retention(RetentionPolicy.SOURCE)      注解只在源码级别有效
  @Retention(RetentionPolicy.CLASS)      注解在字节码即别有效     默认值
  @Retention(RetentionPolicy.RUNTIME)      注解在运行时期有效

@Inherited 表示这个注解可以被子类继承
@Documented 表示当执行javadoc的时候,本注解会生成相关文档

例: 做一个jdbc连接配置的注解版本 

@Target({METHOD,TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface JDBCConfig {
     String ip(); 
     int port() default 3306; 
     String database(); 
     String encoding(); 
     String loginName(); 
     String password(); 
}
import anno.JDBCConfig;

@JDBCConfig(ip = "127.0.0.1", database = "test", encoding = "UTF-8", loginName = "root", password = "admin")
public class DBUtil {
    static {
        try {
            Class.forName("com.mysql.jdbc.Driver");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

通过反射解析注解

让方法可以获取注解的属性值

例:通过反射,获取这个DBUtil这个类上的注解对象

// 1. 获取获取方法上的注解
JDBCConfig config = DBUtil.class.getAnnotation(JDBCConfig.class);

// 2. 获取输出注解信息
String ip = config.ip();
int port = config.port();
String database = config.database();
String encoding = config.encoding();
String loginName = config.loginName();
String password = config.password();

// 就可以使用这些信息了
String url = String.format("jdbc:mysql://%s:%d/%s?characterEncoding=%s", ip, port, database, encoding);
return DriverManager.getConnection(url, loginName, password);

 

用注释简化(取代)配置文件:

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值