欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 财经 > 产业 > Java高级

Java高级

2025/9/26 2:10:28 来源:https://blog.csdn.net/weixin_43543654/article/details/144824847  浏览:    关键词:Java高级

1.反射

每个类都有一个唯一的类对象,该对象是 java.lang.Class 类型。【是 Java 类的元数据(metadata)对象,包含了该类的结构信息和其他相关数据】

获取类对象

1.什么是类对象

public class Daughter extends Parent{        }

public class Son extends Parent {        }

Daughter 、Son 都是 parent对象,区别不同的名称,年龄,速度。

Parent 和 Person都是类,区别在于不同方法,不同属性。

类对象,就是描述类有什么属性,什么方法的。

2.获取类对象

3种方式

1. Class.forName(String className)
2. 类名.class
3. getClass() 方法

在一个JVM中,一种类,只会有一个类对象存在。所以以上三种方式取出来的类对象,都是一样的。

 public static void main(String[] args) throws ClassNotFoundException {Class<?> clazz = Class.forName("com.test.example.Parent");System.out.println(clazz.getName()); //com.test.example.ParentClass<Parent> pClass = Parent.class;System.out.println(pClass.getName());  //com.test.example.ParentParent pp=new Parent();Class<? extends Parent> methordClass = pp.getClass();System.out.println(methordClass.getName());  //com.test.example.ParentSystem.out.println(clazz ==pClass); //true
}

3.获取类对象的时候,会导致类属性被初始化

为 Parent 类增加一个静态属性,并且在静态初始化块里进行初始化。

        按照2中再次执行。无论什么途径获取类对象,都会导致静态属性被初始化,而且只会执行一次。只会显示  [一句 parent静态代码块!]

(除了直接使用 Class c = Parent.class 这种方式,这种方式不会导致静态属性被初始化)

总结:

        静态代码块初始化就一次!

        类名.class 方式不会执行静态代码块!

创建对象

与传统的new 关键字 来获取对象方式不同

反射机制,先获取Parent 的 类对象,再由 类对象获取构造器对象,然后通过构造器对象创建一个对象。

   try {// 类对象Class<?> pClass = Class.forName("com.test.example.Parent");//构造器Constructor pc= pClass.getConstructor();//构造器实例化Parent parent = (Parent) pc.newInstance();System.out.println(parent);} catch (Exception e) {throw new RuntimeException(e);}

访问属性

Parent 中 添加两个属性 

private int age; // 私有
public String smart; // 公用
public String getSmart() {return this.smart;
}
public int getAge() {return this.age;
}

//=================================

try {//对象-实例Parent pObj=new Parent();// 类对象Class<?> pClass = pObj.getClass();//获取 public 修饰的smart字段Field smart = pClass.getField("smart");System.out.println(smart.getName());// 字段名System.out.println(smart.getType());  // 字段属性// 获取private修饰的 age字段Field age = pClass.getDeclaredField("age");System.out.println(age.getName());
    // 因为age字段属性private set方法赋值会报错。必须setAccessible 赋值为true
    age.setAccessible(true);age.set(pObj, 23);System.out.println(pObj.getAge());
} catch (Exception e) {throw new RuntimeException(e);
}

1.对于private修饰的成员,需要使用setAccessible(true)才能修改。

getField和getDeclaredField的区别

这两个方法都是用于获取字段

getField 只能获取public的,包括从父类继承来的字段。

getDeclaredField 可以获取本类所有的字段,包括private的,但是不能获取继承来的字段

调用方法

Parent 中 添加两个set方法

public void setSmart(String smart) { // 公共this.smart = smart;
}private void setAge(int age) { //私有this.age = age;
}

//=====================================================

try {//对象-实例Parent pObj = new Parent();// 类对象Class<?> pClass = pObj.getClass();//获取 public 修饰的 方法Method setSmart = pClass.getMethod("setSmart", String.class);//输出setSmart方法的返回值,这里返回 null,因为 setSmart 没有返回值)Object obj = setSmart.invoke(pObj, "平凡");System.out.println(obj); // nullSystem.out.println(pObj.getSmart()); // 平凡// private 修饰的方法Method ageSet = pClass.getDeclaredMethod("setAge", int.class);// 私有 设置可被访问ageSet.setAccessible(true);ageSet.invoke(pObj, 12);System.out.println(pObj.getAge()); // 12
} catch (Exception e) {throw new RuntimeException(e);
}

有什么用  [约定优于配置,配置优于实现]

反射非常强大,但是学习了之后,会不知道该如何使用,反而觉得还不如直接调用方法来的直接和方便。
需要学习了Spring 的依赖注入,反转控制之后,才会对反射有更好的理解
简单举例:两个业务类 Service1 + Service2 

public class Service1 {public void doService1(){System.out.println("业务方法1");}
}package reflection;public class Service2 {public void doService2(){System.out.println("业务方法2");}
}

非反射方式

public class Test {public static void main(String[] args) {
//      new Service1().doService1();new Service2().doService2();}
}

        当需要从第一个业务方法切换到第二个业务方法的时候,使用非反射方式,必须修改代码,并且重新编译运行,才可以达到效果

反射方式

准备一个spring.txt文件,里面存类名+ 调用的方法名

class=reflection.Service1    # 类的全名称
method=doService1           #方法名

测试类代码

    @SneakyThrowspublic static void main(String[] args) {//从spring.txt中获取类名称和方法名称File springConfigFile = new File("e:\\project\\getwork\\spring.txt");Properties springConfig = new Properties();springConfig.load(new FileInputStream(springConfigFile));String className = springConfig.getProperty("class"); // 返回结果只是StringString methodName = (String) springConfig.get("method"); // 返回结果Object,需要强转//根据类名称获取类对象Class clazz = Class.forName(className);//根据方法名称,获取方法对象Method m = clazz.getMethod(methodName);//获取构造器Constructor c = clazz.getConstructor();//根据构造器,实例化出对象Object obj = c.newInstance();//调用对象的指定方法m.invoke(obj);}

2.注解

将注解与反射放一起,因为反射用于解析注解中的信息。

基本内置注解

@Override 用在方法上,表示这个方法重写了父类的方法,如toString()。
如果父类没有这个方法,那么就无法编译通过

@Deprecated 表示这个方法已经过期,不建议开发者使用。(暗示在将来某个不确定的版本,就有可能会取消掉)

@SuppressWarnings Suppress英文的意思是抑制的意思,这个注解的用处是忽略警告信息。

比如大家使用集合的时候,有时候为了偷懒,会不写泛型,像这样:
List heros = new ArrayList();

那么就会导致编译器出现警告,而加上

@SuppressWarnings({ "rawtypes", "unused" })  

对这些警告进行了抑制,即忽略掉这些警告信息。

@SuppressWarnings 有常见的值,分别对应如下意思
1.deprecation:使用了不赞成使用的类或方法时的警告(使用@Deprecated使得编译器产生的警告);
2.unchecked:执行了未检查的转换时的警告,例如当使用集合时没有用泛型 (Generics) 来指定集合保存的类型; 关闭编译器警告
3.fallthrough:当 Switch 程序块直接通往下一种情况而没有 Break 时的警告;
4.path:在类路径、源文件路径等中有不存在的路径时的警告;
5.serial:当在可序列化的类上缺少 serialVersionUID 定义时的警告;
6.finally:任何 finally 子句不能正常完成时的警告;
7.rawtypes 泛型类型未指明
8.unused 引用定义了,但是没有被使用
9.all:关于以上所有情况的警告。

@FunctionalInterface这是Java1.8 新增的注解,用于约定函数式接口。
函数式接口概念: 如果接口中只有一个抽象方法(可以包含多个默认方法或多个static方法),该接口称为函数式接口。函数式接口其存在的意义,主要是配合 Lambda表达式来使用。

自定义注解

1.非注解方式DBUtil

通常来讲,在一个基于JDBC开发的项目里,都会有一个DBUtil这么一个类,在这个类里统一提供连接数据库的IP地址,端口,数据库名称, 账号,密码,编码方式等信息。

举个例子:获取一个连接数据库test的连接Connection实例

package util;import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;public class DBUtil {static String ip = "127.0.0.1";static int port = 3306;static String database = "test";static String encoding = "UTF-8";static String loginName = "root";static String password = "admin";static{try {Class.forName("com.mysql.jdbc.Driver");// 版本8.0之后用 com.mysql.cj.jdbc.Driver// 自动加载驱动(对于 Java 6 或更高版本不需要显式加载) 无需这段代码意思} catch (ClassNotFoundException e) {e.printStackTrace();}}public static Connection getConnection() throws SQLException {String url = String.format("jdbc:mysql://%s:%d/%s?characterEncoding=%s", ip, port, database, encoding);return DriverManager.getConnection(url, loginName, password);}public static void main(String[] args) throws SQLException {System.out.println(getConnection());}
}

2.自定义注解@JDBCConfig

创建注解类型不使用class也不使用interface,而是使用@interface

public @interface JDBCConfig

元注解:

@Target({METHOD,TYPE}) 表示这个注解可以用用在类/接口上,还可以用在方法上
@Retention(RetentionPolicy.RUNTIME) 表示这是一个运行时注解,即运行起来之后,才获取注解中的相关信息,而不像基本注解如@Override那种不用运行,在编译时eclipse就可以进行相关工作的编译时注解。
@Inherited 表示这个注解可以被子类继承
@Documented 表示当执行javadoc的时候,本注解会生成相关文档

package anno;import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.TYPE;import java.lang.annotation.Documented;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;@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(); 
}

3.注解方式DBUtil

有了自定义注解@JDBCConfig之后,我们就把非注解方式DBUtil改造成为注解方式DBUtil。

package util;import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;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();}}public static Connection getConnection() throws SQLException, NoSuchMethodException, SecurityException {// 相当于解析  取值// 通过反射,获取这个DBUtil这个类上的注解对象JDBCConfig config = DBUtil.class.getAnnotation(JDBCConfig.class);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);}public static void main(String[] args) throws NoSuchMethodException, SecurityException, SQLException {Connection c = getConnection();System.out.println(c);}
}

元注解

 元数据在英语中对应单词 metadata:解释为【为其他数据提供信息的数据】

这样元注解就好理解了,元注解 meta annotation用于注解 自定义注解 的注解。
元注解有这么几种:
@Target
@Retention
@Inherited
@Documented
@Repeatable (java1.8 新增)

@Target 表示注解的位置
可以选择的位置列表如下:
ElementType.TYPE:能修饰类、接口或枚举类型
ElementType.FIELD:能修饰成员变量
ElementType.METHOD:能修饰方法
ElementType.PARAMETER:能修饰参数
ElementType.CONSTRUCTOR:能修饰构造器
ElementType.LOCAL_VARIABLE:能修饰局部变量
ElementType.ANNOTATION_TYPE:能修饰注解
ElementType.PACKAGE:能修饰包@Retention  表示生命周期
RetentionPolicy.SOURCE: 注解只在源代码中存在,编译成class之后,就没了。@Override 就是这种注解。
RetentionPolicy.CLASS: 注解在java文件编程成.class文件后,依然存在,但是运行起来后就没了。@Retention的默认值,即当没有显式指定@Retention的时候,就会是这种类型。
RetentionPolicy.RUNTIME: 注解在运行起来之后依然存在,程序可以通过反射获取这些信息,自定义注解@JDBCConfig 就是这样。@Inherited 表示该注解具有继承性。@Documented  在用javadoc命令生成API文档后,DBUtil的文档里会出现该注解说明@Repeatable 是 Java 8 引入的一个注解,允许你在同一个元素(类、方法、字段等)上使用多个相同类型的注解

@Repeatable 注解是一个元注解,它标注在容器注解上,容器注解是用来包裹重复的注解的。

1. 创建重复注解

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;// 定义一个重复注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Repeatable(Supervisors.class)  // 使用 @Repeatable 注解指定容器注解
public @interface Supervisor {String value();
}

2. 创建容器注解

容器注解是用来存储多个相同类型注解的注解。它必须使用 @interface 定义,并且使用 @Repeatable 来标注。

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Supervisors {Supervisor[] value();  // 容器注解内部包含多个 Supervisor 注解
}

3. 使用重复注解

public class Employee {@Supervisor("Alice")@Supervisor("Bob")public void work() {System.out.println("Working...");}
}

4. 访问重复注解

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;public class Test {public static void main(String[] args) throws Exception {Method method = Employee.class.getMethod("work");// 获取所有 Supervisor 注解Supervisor[] supervisors = method.getAnnotationsByType(Supervisor.class);// 输出注解的值for (Supervisor supervisor : supervisors) {System.out.println(supervisor.value());}}
}

3.hutool

在大家日常工作中,都常常会做如下这些非常繁琐的工作:
日期与字符串转换
文件操作
转码与反转码
随机数生成
压缩与解压
编码与解码
CVS文件操作
缓存处理
加密解密
定时任务
邮件收发
二维码创建
FTP 上传与下载
图形验证码生成
等等等等

工欲善其事必先利其器! Hutool 就是这么一款超级强力的工具类。

任何第三方jar包先导进项目,才能使用。

编码工具

public static void main(String[] args) {// 16进制工具String encodeStrs = HexUtil.encodeHexStr("nihao明天!");System.out.println(encodeStrs);String decodeStr = HexUtil.decodeHexStr(encodeStrs);System.out.println(decodeStr);//转义工具String s1 = "<script>location.href='http://baidu.com';</script>";String s2 = EscapeUtil.escapeHtml4(s1);//转义String s3 = EscapeUtil.unescapeHtml4(s2); // 反转//hash工具 import cn.hutool.core.util.HashUtil;//URL工具URL url = URLUtil.toUrlForHttp("http:/www.baidu.com");System.out.println(url);String url2 = "https://blog.csdn.net/weixin_43543654/article/details/130481664";String urle = URLUtil.encode(url2);String urld = URLUtil.decode(urle);System.out.println(urle);System.out.println(urld);//Base32_64 转换String charset = "utf-8";String content = "模拟课程java教程";String code32 = Base32.encode(content, charset); //编码content = Base32.decodeStr(code32, charset); // 解码String code64 = Base64.encode(content, charset);  //64 编码content = Base64.decodeStr(code64, charset);   // 64解码}

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词