Java入门(超级详细)(三)

本文详细介绍Java入门知识,涵盖枚举、可变参数、反射、注解、Lambda表达式与Stream流、线程、网络和克隆等内容。阐述各特性的概念、特点、使用场景及方法,如反射可在运行时动态操作对象,线程同步能保证数据一致性。

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

Java 入门 (超级详细)-优快云博客

Java入门(超级详细)(二)-优快云博客

一、枚举

枚举(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());
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

#看心情

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值