Java入门(超级详细)(二)-CSDN博客
目录
一、枚举
枚举的特点
枚举的使用
枚举构造函数
枚举字符串
二、可变参数
三、反射
反射的使用场景
获取类对象
1. 使用Class.forName("类的全名")
2. 使用“类.class”
3. 使用“使用对象名.getClass”
4. 使用类加载器
5. 延时加载,即时加载
获取字段
1. 单个公有字段
2. 多个公有字段
3. 单个字段(公,私)
4. 多个字段(公,私)
5. 常用方法
获取方法
1. 单个公有方法
2. 多个公有方法
3. 单个方法(公,私)
4. 多个方法(公,私)
5. 常用方法
获取构造函数
1. 单个公有构造函数
2. 多个公有构造函数
3. 单个构造函数(公,私)
4. 多个构造函数(公,私)
5. 常用方法
四、注解
注解的作用
注解的类型
预定义注解(Java标准库提供)
元注解(注解上的注解)
自定义注解
通过反射获取注解
五、Lambda 表达式,Stream 流
Lambda表达式用法
Stream流用法
1. forEach 循环
2. filter 过滤
3. map 映射
4. sorted 排序
5. limit 限制
6. skip 跳过
7. distinct 去重
8. collect 汇总
9. reduce 归约
10. summaryStatistics 统计
11. allMatch 所有匹配
12. anyMatch 任何匹配
13. noneMatch 不匹配
六、线程
线程的生命周期
创建线程的三种方式
1. 继承Thread类,重写run方法
2. 实现Runnable接口,重写run方法
3. 实现Callable接口,重写call方法
线程常用方法
线程常用属性
线程同步
1. synchronized关键字
2. lock 锁
3. synchronized 和 lock 的区别
七、网络
使用Scoket实现简单聊天
服务器
客户端
八、克隆
1. 浅克隆
2. 深克隆
枚举(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)是指在运行时获取和检查类、对象、方法和字段的属性和类型的能力。反射可以让我们在运行时动态地创建和修改对象,以及调用对象的方法。
* 在运行时动态地创建和修改对象。
* 在运行时动态地调用对象的方法。
* 在运行时动态地获取对象的属性和类型。
* 在运行时动态地创建和修改类。
* 在运行时动态地调用类的方法。
* 在运行时动态地获取类的属性和类型。
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);
}
}
class Student {
}
class Test{
public static void main(String[] args) {
// 获取类对象
Class studentClass = Student.class;
// 获取类名
String name = studentClass.getName();
System.out.println(name);
}
}
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);
}
}
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);
}
}
即时加载: Class.forName("包名.类名(类的全名)"), 对象名.getClass()
延时加载: 类名.clsss, 类加载器(ClassLoader)
class User {
public String name;
}
class Test {
public static void main(String[] args) throws NoSuchFieldException {
// 获取类对象
Class userClass = User.class;
// 获取字段对象
Field field = userClass.getField("name");
System.out.println(field);
}
}
class User {
public String name;
public String password;
}
class Test {
public static void main(String[] args) {
// 获取类对象
Class userClass = User.class;
// 获取字段对象数组
Field[] fields = userClass.getFields();
// 循环字段对象数组
for (Field field : fields) {
System.out.println(field);
}
}
}
class User {
private Integer age;
}
class Test {
public static void main(String[] args) throws NoSuchFieldException {
// 获取类对象
Class userClass = User.class;
// 获取字段对象
Field field = userClass.getDeclaredField("age");
System.out.println(field);
}
}
class User {
public String name;
public String password;
private Integer age;
private Integer gender;
}
class Test {
public static void main(String[] args) {
// 获取类对象
Class userClass = User.class;
// 获取字段对象数组
Field[] declaredFields = userClass.getDeclaredFields();
// 循环字段对象数组
for (Field declaredField : declaredFields) {
System.out.println(declaredField);
}
}
}
class User {
public String name;
}
class Test {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
// 获取类对象
Class 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);
}
}
class User {
public void show() {
}
}
class Test {
public static void main(String[] args) throws NoSuchMethodException {
// 获取类对象
Class userClass = User.class;
// 获取方法对象
Method method = userClass.getMethod("show");
System.out.println(method);
}
}
class User {
public void show1() {
}
public void show2() {
}
}
class Test {
public static void main(String[] args) {
// 获取类对象
Class userClass = User.class;
// 获取方法对象数组
Method[] methods = userClass.getMethods();
// 循环方法对象数组
for (Method method : methods) {
System.out.println(method);
}
}
}
class User {
private void show() {
}
}
class Test {
public static void main(String[] args) throws NoSuchMethodException {
// 获取类对象
Class userClass = User.class;
// 获取方法对象
Method declaredMethod = userClass.getDeclaredMethod("show");
System.out.println(declaredMethod);
}
}
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 userClass = User.class;
// 获取方法对象数组
Method[] declaredMethods = userClass.getDeclaredMethods();
// 循环方法对象数组
for (Method declaredMethod : declaredMethods) {
System.out.println(declaredMethod);
}
}
}
class User {
public String show(String name){
return name;
}
}
class Test {
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
// 获取类对象
Class 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);
}
}
class User {
public User() {
}
}
class Test {
public static void main(String[] args) throws NoSuchMethodException {
// 获取类对象
Class userClass = User.class;
// 获取构造函数对象
Constructor constructor = userClass.getConstructor();
System.out.println(constructor);
}
}
class User {
public User(){
}
public User(String name){
}
public User(String name, String password){
}
}
class Test {
public static void main(String[] args) {
// 获取类对象
Class userClass = User.class;
// 获取构造函数对象数组
Constructor>[] constructors = userClass.getConstructors();
// 循环构造函数对象
for (Constructor> constructor : constructors) {
System.out.println(constructor);
}
}
}
class User {
private User(String name) {
}
}
class Test {
public static void main(String[] args) throws NoSuchMethodException {
// 获取类对象
Class userClass = User.class;
// 获取构造函数对象
Constructor declaredConstructor = userClass.getDeclaredConstructor(String.class);
System.out.println(declaredConstructor);
}
}
class User {
public User(){
}
private User(String name) {
}
private User(String name, String password) {
}
}
class Test {
public static void main(String[] args) {
// 获取类对象
Class userClass = User.class;
// 获取构造函数对象数组
Constructor>[] declaredConstructors = userClass.getDeclaredConstructors();
// 循环构造函数对象数组
for (Constructor> declaredConstructor : declaredConstructors) {
System.out.println(declaredConstructor);
}
}
}
class User {
public User(String name) {
System.out.println("传入的名称:" + name);
}
}
class Test {
public static void main(String[] args) throws Exception {
// 获取类对象
Class userClass = User.class;
// 获取构造方法对象
Constructor 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. 运行时注解:在运行阶段才起作用,甚至会影响运行逻辑的注解。
@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 userClass = User.class;
// 获取注解
MyAnnotation annotation = userClass.getAnnotation(MyAnnotation.class);
// 获取注解中的名称
String name = annotation.name();
System.out.println(name);
}
}
Lambda 表达式(Lambda Expression)是一种匿名函数,它可以用来创建和使用函数式接口。Lambda 表达式可以简化代码,提高代码的可读性和可维护性。语法:(parameters) -> { body }
Stream 是 Java 8 中引入的一种新的集合类型,它可以用来对集合进行流式处理。Stream 提供了一系列的操作方法,可以对集合中的元素进行过滤、映射、聚合等操作。
// 匿名类
Comparator comparator = new Comparator() {
@Override
public int compare(String o1, String o2) {
return o1.compareTo(o2);
}
};
// lambda 表达式
Comparator comparator1 = (o1, o2) -> o1.compareTo(o2);
// list 集合
List list = List.of("a", "b", "c");
// forEach 遍历集合
// list.stream().forEach(System.out::println);
// 简写
list.forEach(System.out::println);
// list 集合
List list = List.of(1, 2, 3, 4, 5);
// 过滤掉偶数
List integers = list.stream().filter(i -> i % 2 == 0).toList();
// 遍历集合
integers.forEach(System.out::println);
// list 集合
List names = List.of("Alice", "Bob", "Charlie");
// 将 names 集合中的名字的长度映射到新的流中
List list = names.stream().map(String::length).toList();
// 遍历集合
list.forEach(System.out::println);
// 数字集合
List numbers = Arrays.asList(5, 2, 4, 1, 3);
// 排序
List sortedNumbers = numbers.stream()
.sorted().toList();
// 遍历
sortedNumbers.forEach(System.out::println);
// 数字集合
List numbers = Arrays.asList(5, 2, 4, 1, 3);
// 限制前三条
List list = numbers.stream().limit(3).toList();
// 遍历
list.forEach(System.out::println);
// 数字集合
List numbers = Arrays.asList(1, 2, 3, 4, 5);
// 跳过前三条
List list = numbers.stream().skip(3).toList();
// 遍历
list.forEach(System.out::println);
// 数字集合
List numbers = Arrays.asList(1, 4, 5, 2, 2, 3, 4, 5);
// 去重
List list = numbers.stream().distinct().toList();
// 遍历
list.forEach(System.out::println);
// 数字集合
List numbers = Arrays.asList(1, 2, 3, 4, 5);
// 计算每个数的平方 汇总到list集合中
List squaredNumbers = numbers.stream()
.map(n -> n * n)
.collect(Collectors.toList());
// 遍历
squaredNumbers.forEach(System.out::println);
// 数字集合
List numbers = Arrays.asList(1, 2, 3, 4, 5);
// 计算总和
int sum = numbers.stream()
.reduce(0, Integer::sum);
System.out.println("总和为: " + sum);
// 数字集合
List 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());
// 数字集合
List numbers = Arrays.asList(1, 2, 3, 4, 5);
// 判断流中所有元素是否大于0
boolean b = numbers.stream().allMatch(i -> i > 0);
System.out.println(b);
// 数字集合
List numbers = Arrays.asList(1, 2, 3, 4, 5);
// 判断流中元素是否存在大于10的元素
boolean b = numbers.stream().anyMatch(i -> i > 10);
System.out.println(b);
// 数字集合
List 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()------- 有执行权 变成垃圾
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();
}
}
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();
}
}
class Thread1 implements Callable {
@Override
public String call() {
return "callable 1";
}
}
class Test{
public static void main(String[] args) {
Thread1 call = new Thread1();
FutureTask 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());
}
}
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());
}
}
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());
}
}
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连接,进行数据的发送和接收。
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)是指创建一个与原始对象具有相同属性的新对象。
浅克隆是通过 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());
}
}
深克隆是通过自定义的方式来实现的,它在克隆对象时不仅复制对象本身,还会递归复制对象的所有引用类型属性。这样,新对象和原始对象的引用类型属性都指向不同的对象,修改新对象的属性不会影响到原始对象。
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());
}
}