目录
一、枚举
枚举(enum)是一种特殊的数据类型,它可以用来表示一组有限的常量。枚举可以用来表示性别、颜色、天气等事物的类型。
枚举的特点
1. 有限的常量集合:枚举是一组有限的常量集合,可以用来表示一组相关的值。
2. 类型安全:枚举类型是一种特殊的类,它们具有明确定义的常量,并且在编译时进行类型检查。 3. 可以添加字段和方法:枚举类型可以包含字段和方法,这使得它们可以拥有更多的行为和属性。 4. 单例模式支持:枚举类型的每个枚举常量都是单例的,只会在内存中存在一个实例。
5. 可以进行比较和排序:枚举常量可以使用 compareTo()
方法进行比较,并且可以使用 ordinal()
方法获取它在枚举中的顺序。
6. 可以使用switch语句:枚举类型可以与switch语句一起使用,使代码更加清晰和可读。
7. 可以通过枚举常量获取枚举对象:可以使用枚举常量的名称或索引来获取对应的枚举对象。
8. 可以迭代枚举常量:可以使用 values()
方法获取枚举类型的所有枚举常量,并进行迭代操作。
枚举的使用
// 定义一个颜色枚举
enum ColorEnum {
YELLOW, BLACK, WHITE, GRAY, LIGHT_GRAY, MAGENTA, RED, GREEN, DARK_GRAY, LIME, BLUE, BROWN, PURPLE, CYAN, ORANGE, PINK;
}
class Test {
public static void main(String[] args) {
// 使用枚举
System.out.println(ColorEnum.RED);
}
}
枚举构造函数
// 定义一个颜色枚举
enum ColorEnum {
YELLOW("黄色"),
BLACK("黑色"),
WHITE("白色"),
GRAY("灰色"),
RED("红色"),
GREEN("绿色"),
BLUE("蓝色"),
ORANGE("橙色"),
PINK("粉色"),
MAGENTA("品红"),
PURPLE("紫色"),
CYAN("青色"),
BROWN("棕色"),
LIGHT_GRAY("浅灰色"),
DARK_GRAY("深灰色");
// 颜色中文名
private final String cnName;
// 枚举构造函数
ColorEnum(String cnName) {
this.cnName = cnName;
}
public String getCnName() {
return cnName;
}
}
class Test {
public static void main(String[] args) {
// 使用枚举
System.out.println(ColorEnum.RED.getCnName());
}
}
枚举字符串
// 性别枚举
enum GenderEnum {
MALE{
@Override
public String toString() {
return "我是男生";
}
},
FEMALE{
@Override
public String toString() {
return "我是女生";
}
};
}
class Test {
public static void main(String[] args) {
System.out.println(GenderEnum.MALE);
}
}
二、可变参数
可变参数(varargs)是指在方法声明中可以传入任意数量的参数。可变参数使用 ...
符号表示,并在参数列表的末尾。
class Test {
// 可变参数
public void show(int... args) {
for (int i : args) {
System.out.println(i);
}
}
public static void main(String[] args) {
Test test = new Test();
System.out.println("---- 传入一个参数 ----");
test.show(1);
System.out.println("---- 传入多个参数 ----");
test.show(1, 3,4);
System.out.println("---- 传入一个int数组 ----");
test.show(new int[]{1, 3, 4,5});
}
}
三、反射
反射(reflection)是指在运行时获取和检查类、对象、方法和字段的属性和类型的能力。反射可以让我们在运行时动态地创建和修改对象,以及调用对象的方法。
反射的使用场景
* 在运行时动态地创建和修改对象。
* 在运行时动态地调用对象的方法。
* 在运行时动态地获取对象的属性和类型。
* 在运行时动态地创建和修改类。
* 在运行时动态地调用类的方法。
* 在运行时动态地获取类的属性和类型。
获取类对象
1. 使用Class.forName("类的全名")
class Student {
}
class Test{
public static void main(String[] args) throws ClassNotFoundException {
// 获取类对象
Class<?> aClass = Class.forName("Student");
// 获取类名
String name = aClass.getName();
System.out.println(name);
}
}
2. 使用“类.class”
class Student {
}
class Test{
public static void main(String[] args) {
// 获取类对象
Class<Student> studentClass = Student.class;
// 获取类名
String name = studentClass.getName();
System.out.println(name);
}
}
3. 使用“使用对象名.getClass”
class Student {
}
class Test{
public static void main(String[] args) {
// 获取类对象
Student student = new Student();
Class<? extends Student> aClass = student.getClass();
// 获取类名
String name = aClass.getName();
System.out.println(name);
}
}
4. 使用类加载器
class Student {
}
class Test{
public static void main(String[] args) throws ClassNotFoundException {
// 获取类对象
ClassLoader classLoader = ClassLoader.getSystemClassLoader();
Class<?> aClass = classLoader.loadClass("Student");
// 获取类名
String name = aClass.getName();
System.out.println(name);
}
}
5. 延时加载,即时加载
即时加载: Class.forName("包名.类名(类的全名)"), 对象名.getClass()
延时加载: 类名.clsss, 类加载器(ClassLoader)
获取字段
1. 单个公有字段
class User {
public String name;
}
class Test {
public static void main(String[] args) throws NoSuchFieldException {
// 获取类对象
Class<User> userClass = User.class;
// 获取字段对象
Field field = userClass.getField("name");
System.out.println(field);
}
}
2. 多个公有字段
class User {
public String name;
public String password;
}
class Test {
public static void main(String[] args) {
// 获取类对象
Class<User> userClass = User.class;
// 获取字段对象数组
Field[] fields = userClass.getFields();
// 循环字段对象数组
for (Field field : fields) {
System.out.println(field);
}
}
}
3. 单个字段(公,私)
class User {
private Integer age;
}
class Test {
public static void main(String[] args) throws NoSuchFieldException {
// 获取类对象
Class<User> userClass = User.class;
// 获取字段对象
Field field = userClass.getDeclaredField("age");
System.out.println(field);
}
}
4. 多个字段(公,私)
class User {
public String name;
public String password;
private Integer age;
private Integer gender;
}
class Test {
public static void main(String[] args) {
// 获取类对象
Class<User> userClass = User.class;
// 获取字段对象数组
Field[] declaredFields = userClass.getDeclaredFields();
// 循环字段对象数组
for (Field declaredField : declaredFields) {
System.out.println(declaredField);
}
}
}
5. 常用方法
class User {
public String name;
}
class Test {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
// 获取类对象
Class<User> userClass = User.class;
// 获取字段对象
Field field = userClass.getField("name");
// 字段修饰符
System.out.println("字段修饰符:" + Modifier.toString(field.getModifiers()));
// 获取字段类型
System.out.println("字段类型:" + field.getType());
// 获取字段名
System.out.println("字段名:" + field.getName());
// 设置字段的值
User user = new User();
field.set(user, "张三");
// 获取字段的值
System.out.println("字段值:" + field.get(user));
// 设置私有字段可访问
field.setAccessible(true);
}
}
获取方法
1. 单个公有方法
class User {
public void show() {
}
}
class Test {
public static void main(String[] args) throws NoSuchMethodException {
// 获取类对象
Class<User> userClass = User.class;
// 获取方法对象
Method method = userClass.getMethod("show");
System.out.println(method);
}
}
2. 多个公有方法
class User {
public void show1() {
}
public void show2() {
}
}
class Test {
public static void main(String[] args) {
// 获取类对象
Class<User> userClass = User.class;
// 获取方法对象数组
Method[] methods = userClass.getMethods();
// 循环方法对象数组
for (Method method : methods) {
System.out.println(method);
}
}
}
3. 单个方法(公,私)
class User {
private void show() {
}
}
class Test {
public static void main(String[] args) throws NoSuchMethodException {
// 获取类对象
Class<User> userClass = User.class;
// 获取方法对象
Method declaredMethod = userClass.getDeclaredMethod("show");
System.out.println(declaredMethod);
}
}
4. 多个方法(公,私)
class User {
public void show1() {
}
private void show2(String name) {
}
private Integer show3() {
return 1;
}
public String show4(String name) {
return name;
}
}
class Test {
public static void main(String[] args) {
// 获取类对象
Class<User> userClass = User.class;
// 获取方法对象数组
Method[] declaredMethods = userClass.getDeclaredMethods();
// 循环方法对象数组
for (Method declaredMethod : declaredMethods) {
System.out.println(declaredMethod);
}
}
}
5. 常用方法
class User {
public String show(String name){
return name;
}
}
class Test {
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
// 获取类对象
Class<User> userClass = User.class;
// 获取方法对象
Method method = userClass.getMethod("show", String.class);
// 获取方法修饰符
System.out.println("修饰符:" + Modifier.toString(method.getModifiers()));
// 获取方法返回值类型
System.out.println("返回值类型:" + method.getReturnType());
// 获取方法名
System.out.println("方法名:" + method.getName());
// 获取方法参数类型
Class<?>[] parameterTypes = method.getParameterTypes();
for (Class<?> parameterType : parameterTypes) {
System.out.println("参数类型:" + parameterType);
}
// 调用方法并传参
Object obj = method.invoke(new User(), "张三");
System.out.println(obj);
// 设置方法可访问性
method.setAccessible(true);
}
}
获取构造函数
1. 单个公有构造函数
class User {
public User() {
}
}
class Test {
public static void main(String[] args) throws NoSuchMethodException {
// 获取类对象
Class<User> userClass = User.class;
// 获取构造函数对象
Constructor<User> constructor = userClass.getConstructor();
System.out.println(constructor);
}
}
2. 多个公有构造函数
class User {
public User(){
}
public User(String name){
}
public User(String name, String password){
}
}
class Test {
public static void main(String[] args) {
// 获取类对象
Class<User> userClass = User.class;
// 获取构造函数对象数组
Constructor<?>[] constructors = userClass.getConstructors();
// 循环构造函数对象
for (Constructor<?> constructor : constructors) {
System.out.println(constructor);
}
}
}
3. 单个构造函数(公,私)
class User {
private User(String name) {
}
}
class Test {
public static void main(String[] args) throws NoSuchMethodException {
// 获取类对象
Class<User> userClass = User.class;
// 获取构造函数对象
Constructor<User> declaredConstructor = userClass.getDeclaredConstructor(String.class);
System.out.println(declaredConstructor);
}
}
4. 多个构造函数(公,私)
class User {
public User(){
}
private User(String name) {
}
private User(String name, String password) {
}
}
class Test {
public static void main(String[] args) {
// 获取类对象
Class<User> userClass = User.class;
// 获取构造函数对象数组
Constructor<?>[] declaredConstructors = userClass.getDeclaredConstructors();
// 循环构造函数对象数组
for (Constructor<?> declaredConstructor : declaredConstructors) {
System.out.println(declaredConstructor);
}
}
}
5. 常用方法
class User {
public User(String name) {
System.out.println("传入的名称:" + name);
}
}
class Test {
public static void main(String[] args) throws Exception {
// 获取类对象
Class<User> userClass = User.class;
// 获取构造方法对象
Constructor<User> constructor = userClass.getConstructor(String.class);
// 获取构造方法的修饰符
System.out.println("修饰符:" + Modifier.toString(constructor.getModifiers()));
// 获取构造方法的名称
System.out.println("名称:" + constructor.getName());
// 获取构造方法的参数类型
for (Class<?> parameterType : constructor.getParameterTypes()) {
System.out.println("参数类型:" + parameterType);
}
// 使用构造方法创建新的实例
User user = constructor.newInstance("张三");
// 设置构造方法的可访问性
constructor.setAccessible(true);
}
}
四、注解
注解(Annotation)是一种代码级别的元数据,它可以附加在类、方法、属性、变量等元素上,用来提供信息。注解可以用于编译时检查、运行时动态处理等方面。语法:@注解名(属性名=属性值,...)
注解的作用
编写文档:通过代码里标识的元数据生成文档【生成文档doc文档】
代码分析:通过代码里标识的元数据对代码进行分析【使用反射】
编译检查:通过代码里标识的元数据让编译器能够实现基本的编译检查【Override】
注解的类型
1. 源码注解:注解只在源码中存在,编译成.class文件就不存在了。
2. 编译时注解:注解在源码和.class文件中都存在
3. 运行时注解:在运行阶段才起作用,甚至会影响运行逻辑的注解。
预定义注解(Java标准库提供)
@Override :表示该方法重写了父类的方法。 @Deprecated :表示该方法或类已过时,不建议使用。 @SuppressWarnings :表示编译器忽略某些警告。 @SafeVarargs :表示该方法使用了可变参数,并且编译器可以安全地进行优化。 @FunctionalInterface :表示该接口是函数式接口。
元注解(注解上的注解)
@Repeatable :表示该注解可以重复使用。 @Documented :表示该注解应该被 Javadoc 文档记录。 @Inherited :表示该注解可以被子类继承。 @Target :表示该注解可以用于哪些类型的元素。 @Retention :表示该注解在什么时候被保留。 @Documented :表示该注解应该被 Javadoc 文档记录。 @Inherited :表示该注解可以被子类继承。 @Target :表示该注解可以用于哪些类型的元素。 @Retention :表示该注解在什么时候被保留。
自定义注解
// 设置target的目标位置
@Target(ElementType.TYPE)
// 设置保留策略
@Retention(RetentionPolicy.RUNTIME)
// 生成doc文档
@Documented
public @interface MyAnnotation {
// 设置参数默认为true
boolean value() default true;
}
1. @Target设置注解可以应用的8个目标位置 1) ElementType.TYPE 可以使用在类和接口上面 2) ElementType.CONSTRUCTOR 可以使用在构造函数上 3) ElementType.FIELD 可以使用在属性上 4) ElementType.LOCAL_VARIABLE 可以使用在局部变量上 5) ElementType.METHOD 可以使用在方法上 6) ElementType.PACKAGE 可以使用在包上 7) ElementType.PARAMETER 可以使用在参数上 8) ElementType.ANNOTATION_TYPE 可以使用在定义的注解上
2. @Retention: 注解的生命周期, RetentionPolicy(保留策略), 1) SOURCE: 源码注解, 只保留在源码中,编译时会丢弃(不保留到class文件中) 2) CLASS: 编译时注解, 只保留在源代码和class文件中,但运行时不会加载到jvm虚拟机 3) RUNTIME: 运行时注解, 保留在源代码和class文件中,运行时存在,可以通过反射读取。
通过反射获取注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@interface MyAnnotation {
String name();
}
@MyAnnotation(name = "用户")
class User{
}
class Test{
public static void main(String[] args) {
// 获取类对象
Class<User> userClass = User.class;
// 获取注解
MyAnnotation annotation = userClass.getAnnotation(MyAnnotation.class);
// 获取注解中的名称
String name = annotation.name();
System.out.println(name);
}
}
五、Lambda 表达式,Stream 流
Lambda 表达式(Lambda Expression)是一种匿名函数,它可以用来创建和使用函数式接口。Lambda 表达式可以简化代码,提高代码的可读性和可维护性。语法:(parameters) -> { body }
Stream 是 Java 8 中引入的一种新的集合类型,它可以用来对集合进行流式处理。Stream 提供了一系列的操作方法,可以对集合中的元素进行过滤、映射、聚合等操作。
Lambda表达式用法
// 匿名类
Comparator<String> comparator = new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return o1.compareTo(o2);
}
};
// lambda 表达式
Comparator<String> comparator1 = (o1, o2) -> o1.compareTo(o2);
Stream流用法
1. forEach 循环
// list 集合
List<String> list = List.of("a", "b", "c");
// forEach 遍历集合
// list.stream().forEach(System.out::println);
// 简写
list.forEach(System.out::println);
2. filter 过滤
// list 集合
List<Integer> list = List.of(1, 2, 3, 4, 5);
// 过滤掉偶数
List<Integer> integers = list.stream().filter(i -> i % 2 == 0).toList();
// 遍历集合
integers.forEach(System.out::println);
3. map 映射
// list 集合
List<String> names = List.of("Alice", "Bob", "Charlie");
// 将 names 集合中的名字的长度映射到新的流中
List<Integer> list = names.stream().map(String::length).toList();
// 遍历集合
list.forEach(System.out::println);
4. sorted 排序
// 数字集合
List<Integer> numbers = Arrays.asList(5, 2, 4, 1, 3);
// 排序
List<Integer> sortedNumbers = numbers.stream()
.sorted().toList();
// 遍历
sortedNumbers.forEach(System.out::println);
5. limit 限制
// 数字集合
List<Integer> numbers = Arrays.asList(5, 2, 4, 1, 3);
// 限制前三条
List<Integer> list = numbers.stream().limit(3).toList();
// 遍历
list.forEach(System.out::println);
6. skip 跳过
// 数字集合
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
// 跳过前三条
List<Integer> list = numbers.stream().skip(3).toList();
// 遍历
list.forEach(System.out::println);
7. distinct 去重
// 数字集合
List<Integer> numbers = Arrays.asList(1, 4, 5, 2, 2, 3, 4, 5);
// 去重
List<Integer> list = numbers.stream().distinct().toList();
// 遍历
list.forEach(System.out::println);
8. collect 汇总
// 数字集合
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
// 计算每个数的平方 汇总到list集合中
List<Integer> squaredNumbers = numbers.stream()
.map(n -> n * n)
.collect(Collectors.toList());
// 遍历
squaredNumbers.forEach(System.out::println);
9. reduce 归约
// 数字集合
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
// 计算总和
int sum = numbers.stream()
.reduce(0, Integer::sum);
System.out.println("总和为: " + sum);
10. summaryStatistics 统计
// 数字集合
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
// 统计
IntSummaryStatistics summaryStatistics = numbers.stream()
.mapToInt(Integer::intValue)
.summaryStatistics();
System.out.println("总数:" + summaryStatistics.getCount());
System.out.println("平均数:" + summaryStatistics.getAverage());
System.out.println("最大值:" + summaryStatistics.getMax());
System.out.println("最小值:" + summaryStatistics.getMin());
System.out.println("总和:" + summaryStatistics.getSum());
11. allMatch 所有匹配
// 数字集合
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
// 判断流中所有元素是否大于0
boolean b = numbers.stream().allMatch(i -> i > 0);
System.out.println(b);
12. anyMatch 任何匹配
// 数字集合
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
// 判断流中元素是否存在大于10的元素
boolean b = numbers.stream().anyMatch(i -> i > 10);
System.out.println(b);
13. noneMatch 不匹配
// 数字集合
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
// 判断流中元素是否没有大于10的元素
boolean b = numbers.stream().noneMatch(i -> i > 10);
System.out.println(b);
六、线程
线程(Thread)是程序执行的基本单元。每个线程都有自己的堆栈和程序计数器,并且可以独立于其他线程运行。线程可以通过共享内存或共享数据库来相互通信。
线程的生命周期
1. **新建**:当创建一个线程对象时,该线程处于新建状态。
2. **就绪**:当线程对象调用 start()
方法时,该线程进入就绪状态。
3. **运行**:当线程获得 CPU 时间片时,该线程进入运行状态。
4. **阻塞**:当线程等待某些条件满足时,该线程进入阻塞状态。
5. **死亡**:当线程执行完毕或被中断时,该线程进入死亡状态。
时间到了 sleep(100毫秒)
-----------没执行资格------------
| 没有执行权 |
| |
| |
创建线程对象----start()----有执行资格-------抢到cup------>有执行资格------run结束-----线程死亡
没有执行权<------yield()------- 有执行权 变成垃圾
创建线程的三种方式
1. 继承Thread类,重写run方法
class Thread1 extends Thread{
@Override
public void run() {
System.out.println("线程1");
}
}
class Test{
public static void main(String[] args) {
Thread1 t = new Thread1();
// 启动线程
t.start();
}
}
2. 实现Runnable接口,重写run方法
class Thread1 implements Runnable{
@Override
public void run() {
System.out.println("runnable 1");
}
}
class Test{
public static void main(String[] args) {
Thread t = new Thread(new Thread1());
// 启动线程
t.start();
}
}
3. 实现Callable接口,重写call方法
class Thread1 implements Callable<String> {
@Override
public String call() {
return "callable 1";
}
}
class Test{
public static void main(String[] args) {
Thread1 call = new Thread1();
FutureTask<String> futureTask = new FutureTask<>(call);
Thread t = new Thread(futureTask);
// 启动线程
t.start();
// 获取线程返回值
try {
System.out.println(futureTask.get());
}catch (Exception e){
e.printStackTrace();
}
}
}
Runnable 和 Callable 的主要区别在于是否有返回值, Callable有返回值
线程常用方法
1. start() :启动线程,使线程进入就绪状态并开始执行。 2. run() :线程的执行逻辑,需要在自定义的线程类中重写该方法。 3. join() :等待线程执行完成,即阻塞当前线程,直到该线程执行完毕。 4. sleep(long millis) :使线程暂停执行指定的毫秒数。 5. yield() :暂停当前线程,让出 CPU 时间片,让其他线程有机会执行。 6. interrupt() :中断线程,给线程发送中断信号,但不一定会立即终止线程。 7. isInterrupted() :判断线程是否被中断。 8. isAlive() :判断线程是否处于活动状态,即线程是否启动且尚未终止。
线程常用属性
1. getName() :获取线程的名称。 2. setName(String name) :设置线程的名称。 3. getPriority() :获取线程的优先级。 4. setPriority(int priority) :设置线程的优先级,范围为 1(最低)到 10(最高)。 5. getState() :获取线程的状态,返回一个 Thread.State 枚举值。 6. isDaemon() :判断线程是否为守护线程。 7. setDaemon(boolean on) :设置线程是否为守护线程。 8. getId() :获取线程的唯一标识符。 9. getThreadGroup() :获取线程所属的线程组。
线程同步
线程同步是指多个线程在访问共享资源时,通过协调彼此的执行顺序来保证数据的一致性和完整性。在多线程环境下,如果多个线程同时访问和修改共享资源,可能会导致数据竞争和不确定的结果。
class Counter {
private int count = 0;
public void increment() {
count++;
}
public void decrement() {
count--;
}
public int getCount() {
return count;
}
}
class Test{
public static void main(String[] args) throws InterruptedException {
Counter counter = new Counter();
// 增加计数器的值
Runnable r1 = () -> IntStream.range(0, 10000).forEach(i -> counter.increment());
// 减少计数器的值
Runnable r2 = () -> IntStream.range(0, 10000).forEach(i -> counter.decrement());
// 创建两个线程
Thread t1 = new Thread(r1);
Thread t2 = new Thread(r2);
// 启动线程
t1.start();
t2.start();
// 等待线程结束
t1.join();
t2.join();
// 最终结果 由于两个线程并发执行,所以最终结果可能不准确
System.out.println("计数器的值: " + counter.getCount());
}
}
1. synchronized关键字
class Counter {
private int count = 0;
// 使用 synchronized 保证线程同步
public synchronized void increment() {
count++;
}
public synchronized void decrement() {
count--;
}
public int getCount() {
return count;
}
}
class Test{
public static void main(String[] args) throws InterruptedException {
Counter counter = new Counter();
// 增加计数器的值
Runnable r1 = () -> IntStream.range(0, 10000).forEach(i -> counter.increment());
// 减少计数器的值
Runnable r2 = () -> IntStream.range(0, 10000).forEach(i -> counter.decrement());
// 创建两个线程
Thread t1 = new Thread(r1);
Thread t2 = new Thread(r2);
// 启动线程
t1.start();
t2.start();
// 等待线程结束
t1.join();
t2.join();
// 最终结果 使用 synchronized 保证线程安全 输出结果为 0
System.out.println("计数器的值: " + counter.getCount());
}
}
2. lock 锁
class Counter {
private int count = 0;
// lock 锁
private Lock lock = new ReentrantLock();
public void increment() {
// 开启锁
lock.lock();
try {
count++;
}finally {
// 关闭锁
lock.unlock();
}
}
public synchronized void decrement() {
lock.lock();
try {
count--;
}finally {
lock.unlock();
}
}
public int getCount() {
return count;
}
}
class Test{
public static void main(String[] args) throws InterruptedException {
Counter counter = new Counter();
// 增加计数器的值
Runnable r1 = () -> IntStream.range(0, 10000).forEach(i -> counter.increment());
// 减少计数器的值
Runnable r2 = () -> IntStream.range(0, 10000).forEach(i -> counter.decrement());
// 创建两个线程
Thread t1 = new Thread(r1);
Thread t2 = new Thread(r2);
// 启动线程
t1.start();
t2.start();
// 等待线程结束
t1.join();
t2.join();
// 最终结果 使用 lock 锁保证线程安全 输出结果为 0
System.out.println("计数器的值: " + counter.getCount());
}
}
3. synchronized 和 lock 的区别
1. **使用方式**: - synchronized 是 Java 语言提供的关键字,可以直接应用于方法或代码块,使用简单方便。 - Lock 是一个接口,需要通过具体的实现类(如 ReentrantLock )来创建锁对象,使用时需要手动调用 lock() 和 unlock() 方法来控制锁的获取和释放。 2. **灵活性**: - synchronized 是隐式锁,在进入同步代码块或方法时自动获取锁,并在退出时自动释放锁。它具有可重入性(同一个线程可以重复获取同一个锁),并且支持监视器锁的等待和唤醒机制。 - Lock 是显示锁,需要手动调用 lock() 方法来获取锁,并在合适的时机调用 unlock() 方法来释放锁。它提供了更灵活的锁获取和释放操作,可以实现更复杂的同步控制。 3. **功能扩展**: - Lock 接口提供了更多的功能扩展,如可中断锁、可轮询锁、公平锁等。它还支持多个条件变量(Condition),可以更精细地控制线程的等待和唤醒。 4. **性能**: - synchronized 是 JVM 内置的机制,经过优化,性能较好。在低竞争情况下,synchronized 的性能表现通常比较好。 - Lock 是基于 Java 语言级别的实现,相对于 synchronized 更灵活,但在竞争激烈的高并发场景下,性能可能更好。 总的来说,synchronized 适用于简单的同步需求,使用方便;而 Lock 则提供了更多的功能和灵活性,适用于复杂的同步控制场景。具体选择哪种方式取决于具体的需求和场景。
七、网络
Socket用于实现网络通信中的客户端,ServerSocket类用于实现网络通信中的服务器端。通过Socket和ServerSocket,可以建立TCP/IP连接,进行数据的发送和接收。
使用Scoket实现简单聊天
服务器
public static void main(String[] args) {
try {
// 新建一个服务器
ServerSocket serverSocket = new ServerSocket(8080);
System.out.println("服务器已启动,等待客户端连接...");
// 等待客户端连接
Socket clientSocket = serverSocket.accept();
System.out.println("客户端已连接:" + clientSocket.getInetAddress());
// 获取输入输出流
InputStream inputStream = clientSocket.getInputStream();
OutputStream outputStream = clientSocket.getOutputStream();
// 读取客户端消息
byte[] buffer = new byte[1024];
int bytesRead;
while ((bytesRead = inputStream.read(buffer)) != -1) {
String message = new String(buffer, 0, bytesRead);
System.out.println("客户端消息:" + message);
String response = "服务器收到消息:" + message;
outputStream.write(response.getBytes());
outputStream.flush();
}
// 关闭连接
clientSocket.close();
serverSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
客户端
public static void main(String[] args) throws IOException {
try {
// 创建客户端
Socket socket = new Socket("localhost", 8080);
System.out.println("已连接到服务器");
// 获取输入输出流
InputStream inputStream = socket.getInputStream();
OutputStream outputStream = socket.getOutputStream();
// 接收服务器的消息
Scanner scanner = new Scanner(System.in);
while (true) {
System.out.print("请输入消息:");
String message = scanner.nextLine();
outputStream.write(message.getBytes());
outputStream.flush();
byte[] buffer = new byte[1024];
int bytesRead = inputStream.read(buffer);
String response = new String(buffer, 0, bytesRead);
System.out.println("服务器回复:" + response);
}
} catch (IOException e) {
e.printStackTrace();
}
}
八、克隆
克隆(Clone)是指创建一个与原始对象具有相同属性的新对象。
1. 浅克隆
浅克隆是通过 clone()
方法来实现的。它创建一个新对象,并将原始对象的属性值复制到新对象中。如果属性是基本数据类型,那么复制的是值本身;如果属性是引用类型,那么复制的是引用,即新对象和原始对象引用同一个对象。因此,在浅克隆中,如果修改新对象的引用类型属性,会影响到原始对象的属性。
public class Person implements Cloneable {
private String name;
private int 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;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public Person clone() throws CloneNotSupportedException {
return (Person) super.clone();
}
public static void main(String[] args) throws CloneNotSupportedException {
Person p1 = new Person("Alice", 20);
Person p2 = p1.clone();
p2.setName("Bob");
p2.setAge(21);
System.out.println(p1.getName() + " " + p1.getAge());
System.out.println(p2.getName() + " " + p2.getAge());
}
}
2. 深克隆
深克隆是通过自定义的方式来实现的,它在克隆对象时不仅复制对象本身,还会递归复制对象的所有引用类型属性。这样,新对象和原始对象的引用类型属性都指向不同的对象,修改新对象的属性不会影响到原始对象。
class Student implements Cloneable {
private String name;
private int age;
private Address address;
public Student(String name, int age, Address address) {
this.name = name;
this.age = age;
this.address = address;
}
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 Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
@Override
public Student clone() throws CloneNotSupportedException {
Student student = (Student) super.clone();
student.address = this.address.clone();
return student;
}
}
class Address implements Cloneable {
private String city;
public Address(String city) {
this.city = city;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
@Override
protected Address clone() throws CloneNotSupportedException {
return (Address) super.clone();
}
}
class Test{
public static void main(String[] args) throws CloneNotSupportedException {
Address address = new Address("New York");
Student student1 = new Student("Alice", 25, address);
Student student2 = student1.clone();
student2.setName("Bob");
student2.setAge(30);
student2.getAddress().setCity("Los Angeles");
System.out.println(student1.getName() + " " + student1.getAge() + " " +student1.getAddress().getCity());
System.out.println(student2.getName() + " " + student2.getAge() + " " +student2.getAddress().getCity());
}
}