Java进阶知识
Java进阶知识
1、java连接数据库
见javaweb笔记
使用JDBC连接数据库
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.19</version>
</dependency>
public class Dao {
private static final String URL = "jdbc:mysql://localhost:3306/course"+
"?useSSL=false&serverTimezone=UTC&characterEncoding=utf-8&allowPublicKeyRetrieval=true";
private static final String USER = "root";
private static final String PASSWORD = "root";
public Connection getConnection() throws SQLException {
try {
Class.forName("com.mysql.cj.jdbc.Driver");
} catch (ClassNotFoundException ex) {
throw new RuntimeException(ex);
}
return DriverManager.getConnection(URL, USER, PASSWORD);
}
// 具体操作
}
2、使用json数据
fastjson库
Fastjson 简明教程 | 菜鸟教程 (runoob.com)
将 Java 对象转换为 JSON 格式
使用 JSON.toJSONString() 将 Java 对象转换换为 JSON 对象
private List<Person> listOfPersons = new ArrayList<Person>();
@Before
public void setUp() {
listOfPersons.add(new Person(15, "John Doe", new Date()));
listOfPersons.add(new Person(20, "Janette Doe", new Date()));
}
@Test
public void whenJavaList_thanConvertToJsonCorrect() {
String jsonOutput= JSON.toJSONString(listOfPersons);
}
3、注解与反射
3.1 Class
3.1.1 内存分析
- 堆内存
- 存放new的对象和数组
- 可以被所有线程共享,不会存放别的对象引用
- 栈内存
- 存放基本变量类型(包含这个基本类型的具体数值)
- 存放引用对象的变量(会存放这个引用在堆里面的具体地址)
- 方法区(特殊的堆)
- 包含所有class和static变量
- 可以被所有线程共享
3.1.2 类的加载过程
当类还未加载到内存时,会进行以下步骤:
- 加载:将class字节码内容加载到内存中,并将静态数据转换成方法区的运行时数据结构,然后生成代表这个类的java.lang.Class对象
- 链接:将java类的二进制代码合并到 JVM 的运行状态之中的过程
- 验证:确保加载的类信息符合 JVM规范
- 准备:正式为类变量(static)分配内存并设置类变量默认初始值的阶段,内存都在方法区中分配
- 解析:虚拟机常量池内的符号引用(常量名)替换为直接引用(地址)的过程
- 类的初始化
- 执行类构造器< clinit>()方法的过程。由编译器自动收集类中所有类变量的赋值动作和静态代码块中的语句合并产生的。(类构造器是构造类信息的,不是构造该类对象的构造器)
- 初始化类时,若父类未初始化,则先触发父类的初始化
- JVM 保证< clinit>()方法在多线程环境中被正确加锁和同步
3.1.3 类加载器
作用:用来把类(Class)装载进内存的
类缓存:某个类被加载到类加载器中,将缓存一段时间,JVM垃圾回收机制可以回收Class对象
分类:
- 引导类加载器:负责Java平台核心库(rt.jar)的加载,c++编写的,无法获取
- 扩展类加载器:加载
lib\ext\
中的jar包 - 系统类加载器(常用)
ClassLoarder systemClassLoader = ClassLoader.getSystemClassLoader(); // 获取系统类加载器
3.2 注解(Annotation)
java.lang.annotation
@注释名
还可以添加参数
内置注解
@Deprecated、@Override等
元注解
创建注解时配置元注解,负责注解自定义注解
- @Retention:定义注解的生命周期
- RetentionPolicy.SOURCE:在编译阶段丢弃,不会写入字节码文件,如@Override等
- RetentionPolicy.CLASS:在类加载时丢弃
- RetentionPolicy.RUNTIME:始终不会丢弃
- @Target:描述注解的使用范围
- @Documented
- @Inherited
自定义注解
- @interface
- 参数成员只能用public或默认
- 参数成员只能使用八种基本数据类型和String、Enum、Class、annotations等,以及这些类型的数组
- 要获取类方法和字段的注解信息,必须通过反射技术获取Annotatuon对象
- 注解可以不定义成员
@Target(value = ElementType.xxx)
@Retention(value = RetentionPolicy.xxx)
@interface 注解名{
//参数类型 参数名();
String name() default ""; // 注解可以显示赋值,若没有默认值,必须赋值
int age() default 0; // 如果只有一个参数成员,建议使用value命名
}
3.3 反射(Reflection)
动态语言:可以在运行时改变其结构,如js,python等
静态语言:运行时结构不可变的语言, 如c++,java
java是准动态语言:反射机制使其获得类似动态语言特性
反射机制允许程序在执行期间借助Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及其方法
加载完类之后,在堆内存的方法区中就产生了一个Class类型的对象(包含完整的类的结构信息)
一个类在内存中只有一个Class对象
public static void main(String[] args) throws ClassNotFountException{ // forName抛出异常
Class<?> a = Class.forName("包名.类名"); // 范型表示未知类型
}
public final Class getClass() // Object类方法,是java反射源头
3.3.1 class类
获取Class类的实例
Class a = C.class; // 已知具体的类,通过类的class属性获取(性能最好)
Class a = c.getClass(); // 已知某类的实例,调用该实例getClass()方法
Class a = Class.forName("包名.类名"); // 已知类全类名,且该类在类路径下,可能抛出ClassNotFountException异常
// 内置对象包装类的Type属性
Class a = Integer.TYPE;
// 获得父类
Class b = c.getSupperclass();
有class对象的类型
class、interface、数组、enum、annotation、基本数据类型、void
只要元素类型与维度一样,就是同一个Class
获取类运行时结构

3.3.2 动态创建对象并执行方法
调用class对象的newInstance()方法
Class class1 = Class.forName("包名.类名");
// 通过无参构造方法
C c1 = (C)class1.newInstance();
// 通过构造器创建对象
Constructor constructor = class1.getDeclaredConstructor(String.class, int,class);
C c2 = (C)class1.newInstance("string", 34);
// 通过反射获取一个方法
Method setName = class1.getDeclaredMethod("方法名", String.class);
setName.invoke(c1, "xxx"); // 执行方法(对象, 方法的值)
// 通过反射操作属性
Field name = class1.getDeclaredField("name");
// 不能直接操作私有属性或方法,需要关闭程序的安全检测
name.setAccessible(true);
name.set(c1, "xxx");
4、lambda表达式
Lambda 表达式是一种简洁的编程方式,能够用来表示实现单个方法的接口(通常是函数式接口)。它能够让代码更加简洁和易读,尤其在处理集合、流操作和回调时非常有用。
基本语法
(参数列表) -> {方法体}
- 参数列表:可以没有参数、一个参数或多个参数。如果只有一个参数并且类型可以推断,括号可以省略。
- 箭头符号
->
:分隔参数和方法体。 - 方法体:可以是一个单独的表达式或者是包含大括号的代码块。
使用 Lambda 表达式的条件
Lambda 表达式主要用于 函数式接口(@FunctionalInterface),也就是只有一个抽象方法的接口。
自定义的函数式接口:
@FunctionalInterface
interface MyFunction {
void myMethod();
}
5、模块化系统
1. 模块化项目的结构
假设你有一个多模块的 JavaFX 项目,比如:
app.main
模块:包含主应用程序。app.ui
模块:包含用户界面相关代码。app.utils
模块:包含实用工具类。
每个模块都有自己的 module-info.java
文件,负责声明该模块的依赖和导出的包。
2.module-info.java
基本语法
2.1 声明模块
每个模块的 module-info.java
文件以 module
关键字声明模块名:
module app.main {
// 模块声明
}
2.2 导入其他模块
如果一个模块依赖于另一个模块,则需要使用 requires
关键字。例如,如果 app.main
需要使用 app.ui
的类,它应该这样声明:
module app.main {
requires app.ui;
requires app.utils;
requires javafx.controls; // 导入 JavaFX 模块
}
2.3 导出包
模块中的包默认是不可见的,如果你希望其他模块能够使用某个模块的包,则需要用 exports
关键字导出包。
exports
:将包的公共类和接口公开给所有模块。opens
:将包中的所有类(包括非公共类和成员)仅对指定的模块开放,主要用于反射。
module app.ui {
exports app.ui.views; // 导出 views 包
}
module app.model {
opens com.example.model to app.main;
}