Java基础

a+=b和a=a+b区别

  • a=a+b不会进行类型的转换,如果不兼容会直接产生报错。a+=b会自动进行类型转换,不会报错,因为会有强转
  • image

JDK8新特性

  • lambda表达式,Stream API流式编程,函数式接口,接口中的默认方法和静态方法,HashMap中的数据结构的变化
  • Stream流只能被消费一次,一次遍历过后就会失效
//1.创建stream
list.stream();         串行流
list.paralleStream();  并行流
Arrays.stream(arr);    给数组创建流
Stream.of("x","y","z");

//2.中间操作
stream.filter(s->s.length()>5);       //筛选操作,筛选出长度大于5的字符串
stream.distinct();                    //去重操作
stream.map(s->s.length());            //映射操作,把String字符串映射成长度Integer
stream.sorted();                        //自然排序,也可以传入比较器,重定义排序
stream.limit(2);                        //保留前n个元素
stream.skip(1);                         //跳过前1个元素


//3.终止操作
收集collect转换成对应的集合
stream.collect(Collectors.toList());
stream.collect(Collectors.toSet());
string joined=stream.collect(Collectors.joining(","));     收集成字符串并且使用,分割

count计数
long count = stream.filter(s -> s.startsWith("a")).count();

是否匹配anyMatch/allMatch
boolean hasApple = stream.anyMatch(s -> s.equals("apple")); // 是否存在"apple"
boolean allLong = stream.allMatch(s -> s.length() > 3); // 是否所有元素长度>3

遍历(forEach):消费每个元素
stream.forEach(s -> System.out.println(s)); // 打印每个元素

Java中内存泄漏的情况

  • 内存泄漏:程序序不再使用的对象,由于仍被可达引用链指向,导致 GC 无法回收,最终占用内存,直至 OOM(内存溢出)。
  • 最常见的是静态集合类,因为他们的生命周期是和JVM一致的,只向里面添加对象不删除,就会导致这些对象一直有静态引用指向无法被GC
  • 缓存没有设置过期策略,比如Map只添加对象没有删除,或者缓存对象过多且没有淘汰机制
  • ThreadLocal
  • 线程池没有正确关闭

静态类和非静态类的区别

  • 静态类static关键词修饰,本身是属于类,在类加载之后就可以调用,不能重写(重写是运行时多态,但是静态类属于类,在编译期就决定调用哪个方法了),不能使用this/super关键字,只能访问静态成员变量(非静态成员属于对象,他是一个类没办法直接调用)

序列化和反序列化

  • 序列化就是把Java对象转换成二进制字节序列(字节数组)的过程,本质是对Java对象的持久化和跨网络传输。反序列化就是把字节序列转换成java对象的过程
  • 所有要序列化的类都必须实现Serializable接口,这个接口里面啥也没有,只是作为一个序列化类的标记
  • 序列化核心类:ObjectOutputStream 类,入口调用writeObject->writeObject0(这个是核心序列化方法),进行空值判断,校验是否实现Serializable接口,处理类的序列化版本用于返序列化的校验,写入类的元数据,写入对象的属性
  • 序列化的规则:1.static静态变量不能被序列化,这是属于类,不属于对象属性。 2.transient 修饰的成员变量不会被序列化,transient关键字专门排除不需要序列化的字段 3.必须要有SerialVersionUID,在序列化的时候会把这个序列号写入字节序列中,反序列化的时候会从字节序列读取到这个序列号,然后和要转换的类的序列号进行比较,如果不相同就不兼容。而且这个序列号必须要手动设置,因为如果不手动设置JVM就会根据这个类的信息进行自动生成,那么如果在序列化之后再对这个类进行了修改,JVM就会给这个类重新生成一个新的序列号,那么在反序列换的时候就会导致两个序列号不一致,
  • 反序列化核心类: ObjectInputStrea类,反序列化操作的入口都是调用 ObjectInputStream 的 readObject() 方法,然后再调用readObject0()方法
private Object readObject0(boolean unshared) throws IOException, ClassNotFoundException {
    // ========== 步骤1:读取字节序列的元数据 ==========
    // 读取序列化时写入的类的全限定名、字段、方法、serialVersionUID等信息
    
    // ========== 步骤2:版本校验 ==========
    // 对比字节序列中的serialVersionUID 和 当前类的serialVersionUID
    // 如果不一致,直接抛出 InvalidClassException 异常 → 版本不兼容
    ObjectStreamClass desc = readClassDesc(false);
    
    // ========== 步骤3:【最核心】创建对象实例(单例被破坏的根源!!!) ==========
    // 重点:这里创建对象,【不会调用类的构造方法】!!!
    // JVM底层通过 反射 + Unsafe类的 allocateInstance() 方法 直接在堆内存中分配空间,创建一个「空对象」
    // 这个空对象没有经过构造方法初始化,绕过了所有构造方法的逻辑(包括单例的私有构造)
    Object obj = desc.isInstantiable() ? desc.newInstance() : null;

    // ========== 步骤4:为创建的空对象赋值 ==========
    // 读取字节序列中保存的属性值,为这个空对象的字段赋值
    // 同样跳过 static/transient 修饰的字段,transient字段赋值为默认值
    readSerialData(obj, desc);

    // ========== 步骤5:可选步骤:调用readResolve()方法 ==========
    // 源码核心:如果当前类中定义了 readResolve() 方法,会调用该方法,返回一个对象
    // 并将这个返回的对象,替换掉上面创建的空对象 → 这是【解决单例被破坏的核心方案】
    if (obj != null && handles.lookupException(passHandle) == null && desc.hasReadResolveMethod()) {
        Object rep = desc.invokeReadResolve(obj);
        obj = rep;
    }
    
    // ========== 最终结果 ==========
    // 返回最终的对象,完成反序列化
    return obj;
}
  • 操作使用
        // ========== 1. 序列化:将User对象写入文件 ==========
        String filePath = "user.txt";
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(filePath));
        User user = new User("张三", 25, "北京市朝阳区");
        oos.writeObject(user); // 执行序列化
        oos.close();

        // ========== 2. 反序列化:从文件中读取对象 ==========
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filePath));
        User user2 = (User) ois.readObject(); // 执行反序列化
        ois.close();

Unsafe类

  • Java本身是安全的,不允许直接操作内存地址和硬件指令,但是Java提供了Unsafe类通过native方法可以和底层的操作系统和硬件交互。
  • 这个类可以分配内存,释放内存(他分配的内存是不被GC回收的,需要手动释放),可以定位对象的内存位置,修改对象值即便是私有的,可以将线程进行挂起和回复,CAS操作
posted @ 2026-01-05 10:05  Huangyien  阅读(14)  评论(0)    收藏  举报