文章目录

  • 1.java语言基础
  • 1.1 java 语言有什么特点😭
  • 1.2. java和c++区别😭
  • 1.3 Oracle JDK 和 OpenJDK 的对⽐
  • 1.4 关于 JVM JDK 和 JRE 最详细通俗的解答❤️😭😭
  • 1.5 八种数据类型⭐️😭😭
  • 1.6 自动拆箱自动装箱
  • 1.7java中方法的参数传递机制⭐️😭
  • 1.8 == 和equals 以及HashCode⭐️⭐️
  • 1.8.1 == 和 equals 区别
  • 1.8.2 hashCode的作用
  • 1.8.3 hashCode() 重写 equals为什么必须要重写?
  • 1.9 java和python区别
  • 1.10 public protected default private的作用范围😭
  • 2. 面向对象
  • 2.1 简述面向对象的三大特性⭐️⭐️⭐️❤️❤️😭
  • 2.2 抽象类和接口的区别⭐️⭐️😭😭😭
  • 2.3 重载和重写的区别⭐️⭐️😭😭😭
  • 3. 关键字
  • 3.1 final关键字 ⭐️⭐️
  • 3.2 static 关键字作用是什么 ⭐️
  • 3.2.1初始化顺序
  • 4.java常用类
  • 4.1 String类
  • 4.1 String底层原理(不可变)⭐️
  • 4.2 Sring不可变得好处
  • 4.3 string和StringBuilder和StringBuffer的区别⭐️⭐️
  • 4.4 String str2 = new String("abc") 创建几个String对象?
  • 4.5String s="a"+"b"+"c"+"d";创建了几个对象?
  • 5.异常😭
  • 5.1 异常分类
  • 5.2 try-catch-finally
  • 5.3 try-catch 中有return的情况
  • 6.java反射机制
  • 6.1 反射是什么,原理是什么?⭐️
  • 6.1 反射的应用场景⭐️
  • 6.3 反射的优缺点?😭
  • 6.3 获取类信息的方法
  • 6.4 利用反射实例化一个对象⭐️
  • 6.5 代理
  • 6.5.1静态代理😭
  • 6.5.2 动态代理⭐️⭐️⭐️😭
  • 6.5.3 JDK 动态代理和 CGLIB 动态代理对比⭐️
  • 7.java集合
  • 7.1 hashmap⭐️⭐️
  • 7.1.1 hashmap 和 hashtable 区别⭐️⭐️😭
  • 7.2 ArrayList
  • 7.2.1 ArrayList和LinkedList区别⭐️⭐️⭐️😭😭
  • 7.3 HashSet
  • 7.3.1 HashSet 和 HashMap 的区别
  • 8.object有哪些方法⭐️
  • 8.1equals()
  • 8.2hashcode()
  • 8.3toString()
  • 8.4clone()
  • 8.4.1 什么是深拷贝,什么是浅拷贝?⭐️❤️
  • 8.4.2 深拷贝的方法是什么❤️
  • 9.IO流😭
  • 9.1 IO流怎么分类?
  • 9.2 基于对象操作(对象流)
  • 9.2.1什么是序列化,什么是反序列化,使用的什么方法⭐️❤️❤️
  • 9.2.2 某些字段不想使用序列化
  • 9.3 访问文件
  • 9.3.1 读取文件和写入文件
  • 9.3.3字节流和字符流区别
  • 9.4 缓冲流
  • 9.4.1 如何实现一行一行读取
  • 9.4.1 io流中的设计模式⭐️
  • 9.5 转换流
  • 10.泛型
  • 10.1 什么是泛型,泛型的好处是什么
  • 10.2 泛型擦除


1.java语言基础

1.1 java 语言有什么特点😭

  • 简单安全可靠面向对象支持多线程和 网络编程

1.2. java和c++区别😭

  • java没有指针
  • Java 有⾃动内存管理机制,不需要程序员⼿动释放⽆⽤内存😭
  • Java 的类是单继承的,C++ ⽀持多重继承;虽然 Java 的类不可以多继承,但是接⼝可以多 继承。

答题思路:三点 指针 继承 垃圾回收

1.3 Oracle JDK 和 OpenJDK 的对⽐

  1. 在响应性和 JVM 性能⽅⾯,Oracle JDK 与 OpenJDK 相⽐提供了更好的性能;
  2. Oracle JDK ⽐ OpenJDK 更稳定。

1.4 关于 JVM JDK 和 JRE 最详细通俗的解答❤️😭😭

  • JRE 是 Java 运⾏时环境。包括 Java 虚拟机 (JVM),Java 类库,java 命令和其他的⼀些基础构件,它不能⽤于创建新程序
  • 拥有 JRE 所拥有的⼀切,还有编 译器和⼯具。它能够创建和编译程序。

1.5 八种数据类型⭐️😭😭

  1. 7 种数字类型 :byte1、char2short2😭int4、float4,long😭8、``、double8`
  2. 1 种布尔型:boolean

1.6 自动拆箱自动装箱

  • 自动拆箱:Integer->int 调用的实际上是a.intValue()😭
  • 自动装箱:int -> Integer 调用的实际上是Integer.valueOf()😭
    new Integer(123) 与 Integer.valueOf(123) 的区别在于:
  • new Integer(123) 每次都会新建一个对象;
  • Integer.valueOf(123) 会使用缓存池中的对象,多次调用会取得同一个对象的引用。
Integer a= 100;(和Integer.valueOf()相等)
Integer b= 100;
Integer c= 200;
Integer d= 200;
System.out.println(a == b);
System.out.println(c == d);

true false

对于 Integer var = ? 在-128 至 127 范围内的赋值,Integer 对象是在IntegerCache.cache 产生,会复用已有对象,这个区间内的 Integer 值可以直接使用==进行判断,但是这个区间之外的所有数据,都会在堆上产生,并不会复用已有对象,这是一个大坑, 推荐使用 equals 方法进行判断。

所有的相同类型的包装类对象之间值的比较,全部使用 equals 方法比较。 - 阿里巴巴手册😭

1.7java中方法的参数传递机制⭐️😭

  • 值传递 JAVA 编程语言只有值传递参数机制
  • 如果参数是基本数据类型(包括对应的包装类型😭),那么参数传递时只是原始值的一份拷贝或者叫副本,对副本操作并不影响原始值;
  • 如果参数是对象(引用类型,包括数组),那么参数传递时传递的是对象的地址的一份拷贝,所以对对象内部属性的操作会改变原始对象相应值,但是对该参数进行重新赋值并不会影响原始对象,使用数组举例子;😭
  • String类型作为参数进行传递时,传递的也是地址。但是String类比较特殊,对参数进行concat,replace,’+‘等操作时不影响原始的串,对参数重新赋值当然也不影响原始字符串😭。

值传递机制

答题思路 : 只有值传递机制 -> 简要介绍各个部分不同的传递情况 (3部分)

1.8 == 和equals 以及HashCode⭐️⭐️

1.8.1 == 和 equals 区别

  • == : (基本数据 类型⽐较的是值,引⽤数据类型⽐较的是内存地址)。😭
  • equals() : 它的作⽤也是判断两个对象是否相等,是在object类内的。但它⼀般有两种使⽤情况:

情况 1:类没有覆盖 equals() ⽅法。则通过 equals() ⽐较该类的两个对象时,等价于通过 “==”⽐较这两个对象。 (源码得出)😭

情况 2:类覆盖了 equals() ⽅法(有String类,以及包装类,建议一般都要使用equals,阿里巴巴技术手册,举例子,Integer类)⼀般,我们都覆盖 equals() ⽅法来⽐较两个对象的内容是 否相等;若它们的内容相等,则返回 true (即,认为这两个对象相等)。

1.8.2 hashCode的作用

**hashCode() 在散列表中才有用,在其它情况下没用。**在散列表中hashCode() 的作用是获取对象的散列码(根据对象的内存地址转化而来的),进而确定该对象在散列表中的位置。

1.8.3 hashCode() 重写 equals为什么必须要重写?

HashCode相等,equals不一定相同,但是如果equals相同,hashcode一定相同。有这个特性,要保持。举了map的例子,存放的时候先进行比较hashcode然后再比较equals,如果只重写一个的话,对象比较的时候会出错。

1.9 java和python区别

Python 开发周期短,容易好学

java效率高

1.10 public protected default private的作用范围😭

public:所有类均可访问

protected:子类及同一个包中类可以访问 😭

default:同一包中的类可以访问 😭

private:只有自己可以访问

2. 面向对象

2.1 简述面向对象的三大特性⭐️⭐️⭐️❤️❤️😭

  • 封装:封装把⼀个对象的属性私有化,同时提供⼀些可以被外界访问的属性的⽅法,不允许直接进行访问而是使用get,set方法进行访问😭
    意义:1.别人不能够通过 变量名.属性名 的方式来修改某个私有的成员属性,提高数据安全性
    2.可以隐藏内部细节,只提供修改的方法。
  • 继承:主要有以下四点
    子类可以对父类进行扩展,而不能选择性继承(父类的私有属性和方法无法访问)😭😭😭
    ⼦类可以⽤⾃⼰的⽅式实现⽗类的⽅法,即重写
    java只允许单继承
    目的:通过使用继承,可以快速地创建新的类,可以提高代码的重用,节省大量创建新类的时间 ,提高我们的开发效率。
  • 多态
    编译时多态主要指方法的重载,即通过参数列表的不同来区分不同的方法。
    运行时多态主要指继承父类和实现接口时,可使用父类引用指向子类对象。
    运行时多态满足几个条件:重写,继承关系,之类的对象指向父类的引用,在使用多态后的父类引用变量调用方法时,会调用子类重写后的方法,编译看左边,运行看右边。
    举例子:我们有一个animals内,都有一个方法叫eat,我们有一些子类进行继承之后,重写了,我们把子类的对象赋给父类的引用,每一个调用方法时候调用的是子类的方法同一个方法调用不同的对象有不同的表现。也就是说多态在编译的时候并不知道,只有在运行的时候才知道指向的是哪个对象,执行的是哪个子类的方法
    好处:提高了扩展性,可以把不同的子类对象都当作父类来看,可以屏蔽不同子类对象之间的差异,写出通用的代码,做出通用的编程。

2.2 抽象类和接口的区别⭐️⭐️😭😭😭

接口

抽象类

方法

不能被实现(1.8以后可以有实现的方法),默认是public

需要被实现否则也是抽象类,可以是抽象的方法也可以是抽象的方法,不能是private

变量

都是被默认的public static final 修饰的

无所谓

关系

通过类来实现,一个类可以实现多个接口,一个接口可以由多个类实现

不能被实例化,只能被继承,一个类只能继承一个抽象类

2.3 重载和重写的区别⭐️⭐️😭😭😭

重载:重载就是同一个类中多个同名方法根据不同的传参来执行不同的逻辑处理。

重写发生在运行期,是子类对父类的允许访问的方法的实现过程进行重新编写。

重载

重写

什么情况下发生

发生在一个类中,例如构造器,方法

发生在子类中

方法名

相同

相同

参数

必须不相同

必须相同

返回类型😭

可以修改

子类方法的返回类型必须是父类方法返回类型或为其子类型。

访问修饰符

可以修改

子类方法的访问权限必须大于等于父类方法

异常😭

可以修改

子类方法抛出的异常类型必须是父类抛出异常类型或为其子类型

答题思路:重写:两小一大 重载:均可以修改

3. 关键字

3.1 final关键字 ⭐️⭐️

联系string

修饰

效果

修饰方法

无法被重写

修饰类

无法被继承

修饰变量

必须有初始值,只能被赋值一次不能被更改😭

3.2 static 关键字作用是什么 ⭐️

随着类的加载而加载也就是,说静态会随着类的消失而消失,说明静态的生命周期最长 ,静态方法只能访问静态成员(包括成员变量和成员方法) 非静态方法可以访问静态也可以访问非静态

修饰

效果

修饰方法

使用类.方法名调用,使用synchroized进行加锁的时候会有冲突

修饰静态代码块

类加载机制初始化时候会进行加载,类初次加载时候加在一次

修饰变量

所有的实例共享,类.变量访问

修饰类

3.2.1初始化顺序

静态变量和静态语句块优先于实例变量和普通语句块,静态变量和静态语句块的初始化顺序取决于它们在代码中的顺序,最后是构造

4.java常用类

4.1 String类

4.1 String底层原理(不可变)⭐️

String 被声明为 final,因此它不可被继承。value 数组被声明为 final,这意味着 value 数组初始化之后就不能再引用其它数组。并且 String 内部没有改变 value 数组的方法,因此可以保证 String 不可变。

4.2 Sring不可变得好处

1. 可以缓存 hash 值

因为 String 的 hash 值经常被使用,例如 String 用做 HashMap 的 key。不可变的特性可以使得 hash 值也不可变,因此只需要进行一次计算。

2. String Pool 的需要

如果一个 String 对象已经被创建过了,那么就会从 String Pool 中取得引用。只有 String 是不可变的,才可能使用 String Pool。

4.3 string和StringBuilder和StringBuffer的区别⭐️⭐️

  • String是不可变的(final修饰的char数组) StringBuilder 和StringBuffer是可变的字符串对象(无final修饰的char数组3)
  • StringBuilder是线程不安全的,stringBuffer是线程安全的(添加字符方法(append)上加上了synchrozied)
  • 实际使用 :字符串改动少用string,线程安全且字符串有改动用stringbuilder,线程不安全且有改动用stringBuffer

4.4 String str2 = new String(“abc”) 创建几个String对象?

String str1 = "abc";  // 在常量池中

String str2 = new String("abc"); // 在堆上

当直接赋值时,字符串“abc”会被存储在常量池中,只有1份,此时的赋值操作等于是创建0个或1个对象。如果常量池中已经存在了“abc”,那么不会再创建对象,直接将引用赋值给str1;如果常量池中没有“abc”,那么创建一个对象,并将引用赋值给str1。

那么,通过new String(“abc”);的形式又是如何呢?答案是1个或2个。

当JVM遇到上述代码时,会先检索常量池中是否存在“abc”,如果不存在“abc”这个字符串,则会先在常量池中创建这个一个字符串。然后再执行new操作,会在堆内存中创建一个存储“abc”的String对象,对象的引用赋值给str2。此过程创建了2个对象。

当然,如果检索常量池时发现已经存在了对应的字符串,那么只会在堆内创建一个新的String对象,此过程只创建了1个对象

4.5String s=“a”+“b”+“c”+“d”;创建了几个对象?

1个

Java 编译器对字符串常量直接相加的表达式进行优化,不等到运行期去进行加法运算,在编译时就去掉了加号,直接将其编译成一个这些常量相连的结果。

所以 “a”+“b”+“c”+“d” 相当于直接定义一个 “abcd” 的字符串。

5.异常😭

5.1 异常分类

在 Java 中,所有的异常都有一个共同的祖先 java.lang 包中的 Throwable 类。Throwable 类有两个重要的子类 Exception(异常)和 Error(错误)。

  • Exception :程序本身可以处理的异常,可以通过 catch 来进行捕获。Exception 又可以分为 受检查异常(比如classnotfoundexception)和不受检查异常不需要被处理(数组越界,和空指针等等)。
  • ErrorError 属于程序无法处理的错误 ,我们没办法通过 catch 来进行捕获 。OOM和stackoverflow,oom和stackoverflow怎么处理

5.2 try-catch-finally

  • try块: 用于捕获异常
  • catch块: 用于处理 try 捕获到的异常
  • finally 块: 无论是否捕获或处理异常,finally 块里的语句都会被执行。

例如lock时候,或者在io流关闭的时候

5.3 try-catch 中有return的情况

当在 try 块或 catch 块中遇到 return 语句时,finally 语句块将在方法返回之前被执行。

return是先记下需要return的东西,然后执行finally,然后return

6.java反射机制

6.1 反射是什么,原理是什么?⭐️

反射:Java反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为Java语言的反射机制。

主要实现类:Class类:可获得类属性方法。Field类:获得类的成员变量。Method类:获取类的方法信息

原理:类加载,Java的反射就是利用加载到jvm中的.class文件来进行操作的。.class文件中包含java类的所有信息,当你不知道某个类具体信息时,可以使用反射获取class,然后进行各种操作。

6.1 反射的应用场景⭐️

spring 的动态代理就利用了反射获取指定的方法

6.3 反射的优缺点?😭

优点:反射提高了扩展性,降低耦合性。通过反射你可以获取任意一个类的所有属性和方法。 为各种框架提供开箱即用的功能提供了便利

缺点:

1.因此反射操作的效率要比非反射操作低

2.反射允许代码执行一些在正常情况下不被允许的操作(比如访问私有的属性和方法)

6.3 获取类信息的方法

1.知道具体类的情况下可以使用:

Class alunbarClass = TargetObject.class;

2.通过对象实例instance.getClass()获取:**

TargetObject o = new TargetObject();
Class alunbarClass2 = o.getClass();

3.通过class.forName()获取,里面是包名类名

6.4 利用反射实例化一个对象⭐️

class1 = Class.forName("net.xsoftlab.baike.User");
// 第一种方法,实例化默认构造方法,调用set赋值
User user = (User) class1.newInstance();

6.5 代理

**代理模式的主要作用是扩展目标对象的功能,比如说在目标对象的某个方法执行前后你可以增加一些自定义的操作。**假设现在项目经理有一个需求:在项目现有所有类的方法前后打印日志。

6.5.1静态代理😭

静态代理实现步骤:

  1. 定义一个接口及其实现类;
  2. 创建一个代理类同样实现这个接口
  3. 将目标对象注入进代理类😭,然后在代理类的对应方法调用目标类中的对应方法。这样的话,我们就可以通过代理类屏蔽对目标对象的访问,并且可以在目标方法执行前后做一些自己想做的事情。
public class Main {
    public static void main(String[] args) {
        SmsService smsService = new SmsServiceImpl();
        SmsProxy smsProxy = new SmsProxy(smsService);
        smsProxy.send("java");
    }
}

缺点:每一个目标类编写对应的代理类如何少写或者不写代理类,却能完成代理功能

6.5.2 动态代理⭐️⭐️⭐️😭

动态代理实现步骤:

能否不写代理类,而直接得到代理Class对象,然后根据它创建代理实例(反射)

JDK代理:

  1. 定义一个接口及其实现类;
  2. 实现 InvocationHandler 并重写invoke方法,在 invoke 方法中我们会调用原生方法(被代理类的方法)并自定义一些处理逻辑;
  3. 通过 Proxy.newProxyInstance 方法创建代理对象;

CGLIB 动态代理类使用步骤:

  1. 定义一个类;
  2. 自定义 MethodInterceptor 并重写 intercept 方法,intercept 用于拦截增强被代理类的方法,和 JDK 动态代理中的 invoke 方法类似;
  3. 通过 Enhancer 类的 create()创建代理类的对象;

6.5.3 JDK 动态代理和 CGLIB 动态代理对比⭐️

  1. JDK 动态代理只能只能代理实现了接口的类或者直接代理接口,而 CGLIB 可以代理未实现任何接口的类。
  2. 就二者的效率来说,大部分情况都是 JDK 动态代理更优秀,随着 JDK 版本的升级,这个优势更加明显。

7.java集合

7.1 hashmap⭐️⭐️

转容器复习笔记

7.1.1 hashmap 和 hashtable 区别⭐️⭐️😭

比较项

Hashmap

HashTable

线程是否安全


是(synchrozied)

效率



是否支持 null key 和 null value😭



初始容量和扩容大小

16/2n (大于它的2次幂)

11/2n + 1 (给定值)😭

底层😭

数组 + 链表 + 红黑树

数组

  1. 线程是否安全:hashmap 是非线程安全的,hashtable 线程安全,所有的方法用 synchrozied 修饰
  2. 效率: 因为线程安全的问题, HashMap 要⽐ 基本被淘汰,不要在代码中使⽤它;HashTable效率高⼀点。
  3. 对 Null key 和 Null value 的⽀持: HashMap 可以存储 null 的 key 和 value,但 null 作为 键只能有⼀个,null 作为值可以有多个;HashTable 不允许有 null 键和 null 值,否则会抛出 NullPointerException 。
  4. 初始容量⼤⼩和每次扩充容量⼤⼩的不同 : ① 创建时如果不指定容量初始值, Hashtable 默认的初始⼤⼩为 11,之后每次扩充,容量变为原来的 2n+1。 HashMap 默认的初始化⼤ ⼩为 16。之后每次扩充,容量变为原来的 2 倍。② 创建时如果给定了容量初始值,那么 Hashtable 会直接使⽤你给定的⼤⼩,⽽ HashMap 会将其扩充为 2 的幂次⽅⼤⼩ ( HashMap 中的 tableSizeFor() ⽅法保证,下⾯给出了源代码)。也就是说 HashMap 总 是使⽤ 2 的幂作为哈希表的⼤⼩,后⾯会介绍到为什么是 2 的幂次⽅。
  5. 底层数据结构: JDK1.8 以后的 HashMap 在解决哈希冲突时有了较⼤的变化,当链表⻓度 ⼤于阈值(默认为 8)(将链表转换成红⿊树前会判断,如果当前数组的⻓度⼩于 64,那么 会选择先进⾏数组扩容,⽽不是转换为红⿊树)时,将链表转化为红⿊树,以减少搜索时 间。Hashtable 没有这样的机制

7.2 ArrayList

7.2.1 ArrayList和LinkedList区别⭐️⭐️⭐️😭😭

  • 增删改查的效率(时间):
    add操作都是o(1)
    添加到指定位置都是o(n)(解释)😭
    随机访问(get):LinkedList不支持高效的随机访问,ArrayList支持高速的随机访问
  • 内存占用(空间):arrayList浪费是主要体现在会在结尾预留空间,LinkedList体现在每一个数多两个指针😭
  • 线程安全吗:都是非线程安全的
  • 扩容问题(底层):ArrayList 使用数组实现,无参构造函数默认初始化长度为 10,数组扩容是会将原数组中的元素重新拷贝到新数组中,长度为原来的 1.5 倍(扩容代价高)😭;LinkedList 不存在扩容问题,新增元素放到集合尾部,修改相应的指针节点即可。😭

7.3 HashSet

7.3.1 HashSet 和 HashMap 的区别

HashMap

HashSet

实现了 Map 接口

实现 Set 接口

存储键值对

仅存储对象

调用 put()向 map 中添加元素

调用 add()方法向 Set 中添加元素

HashMap 使用键(Key)计算 hashcode

HashSet 使用成员对象来计算 hashcode 值,对于两个对象来说 hashcode 可能相同,所以equals()方法用来判断对象的相等性

支持添加 key 和value都为null

支持添加 null 值

hashmap

基于 hashMap 实现,查询很快

无序

无序

8.object有哪些方法⭐️

8.1equals()

8.2hashcode()

8.3toString()

8.4clone()

8.4.1 什么是深拷贝,什么是浅拷贝?⭐️❤️

  • 加入我们要拷贝的对象是Person对象,里面有基本数据类型年龄,和引用数据类型地址,我们要拷贝这个对象调用对象的 clone 方法,必须要让类实现 Cloneable 接口,并且覆写 clone 方法。如果其中有,如果字段是值类型的,那么对该字段执行复制。如果该字段是引用类型的话,则复制引用但不复制引用的对象。因此,原始对象及其副本引用同一个对象。意思就是我们修改克隆后的对象的引用类型的数据,两个都会发生变化
  • 创建一个新对象,然后将当前对象的非静态字段复制到该新对象,无论该字段是值类型的还是引用类型,都复制独立的一份。当你修改其中一个对象的任何内容时,都不会影响另一个对象的内容。

8.4.2 深拷贝的方法是什么❤️

引用类型的化不仅仅复制引用,对象也进行复制,那么该如何实现深拷贝呢?Object 类提供的 clone 是只能实现 浅拷贝的。

1.使用clone方法,除了对这个对象进行克隆,而且需要对这个对象里面的引用变量也进行clone,重写clone方法,这样下去,有多少个引用类型,我们就要重写多少次,如果存在很多引用类型,那么代码量显然会很大,所以这种方法不太合适。

2.序列化:序列化是将对象写到流中便于传输,而反序列化则是把对象从流中读取出来。这里写到流中的对象则是原始对象的一个拷贝,因为原始对象还存在 JVM 中,所以我们可以利用对象的序列化产生克隆对象,然后通过反序列化获取这个对象。

9.IO流😭

9.1 IO流怎么分类?

  • 按照流的流向分,可以分为输入流和输出流;
  • 按照操作单元划分,可以划分为字节流和字符流;
  • 按照流的角色划分为节点流和处理流。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-srR8gjVK-1631847537125)(/Users/yazhouheilong/Library/Application Support/typora-user-images/截屏2021-09-07 14.34.03.png)]

9.2 基于对象操作(对象流)

9.2.1什么是序列化,什么是反序列化,使用的什么方法⭐️❤️❤️

序列化:把对象写到io流内ObjectOutputStream.writeObject()

反序列化:从io流内恢复对象:ObjectInputStream.readObject()

首先想要实现io流的对象需要实现Serializable 接口

public static void main(String[] args) throws IOException, ClassNotFoundException {

    A a1 = new A(123, "abc");
    String objectFile = "file/a1";

    ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream(objectFile));
    objectOutputStream.writeObject(a1);
    objectOutputStream.close();

    ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(objectFile));
    A a2 = (A) objectInputStream.readObject();
    objectInputStream.close();
    System.out.println(a2);
}

private static class A implements Serializable {

    private int x;
    private String y;

    A(int x, String y) {
        this.x = x;
        this.y = y;
    }

    @Override
    public String toString() {
        return "x = " + x + "  " + "y = " + y;
    }
}

9.2.2 某些字段不想使用序列化

transient 关键字的作用是:阻止实例中那些用此关键字修饰的的变量序列化;当对象被反序列化时,被 transient 修饰的变量值不会被持久化和恢复。

  • transient 只能修饰变量,不能修饰类和方法。
  • transient 修饰的变量,在反序列化后变量值将会被置成类型的默认值。例如,如果是修饰 int 类型,那么反序列后结果就是 0
  • static 变量因为不属于任何对象(Object),所以无论有没有 transient 关键字修饰,均不会被序列化。

9.3 访问文件

字节流:FileInputStream,FileOutPutStream

字符流:FileReader,FileWriter

9.3.1 读取文件和写入文件

可以通过字符流和字节流进行读取文件,和写入文件

例如使用字符流

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iLwcL0sQ-1631847537126)(/Users/yazhouheilong/Library/Application Support/typora-user-images/截屏2021-09-07 16.08.54.png)]

9.3.3字节流和字符流区别

  • 在字节流中输出数据主要是使用OutputStream完成,输入使的是InputStream,操作的基本单位是字节,在字符流中输出主要是使用Writer类完成,输入流主要使用Reader类完成,操作的基本单位是字符
  • 如果音频文件、图片等媒体文件用字节流比较好,如果涉及到字符的话使用字符流比较好。
  • 字节流在操作时本身不会用到缓冲区,是文件本身直接操作的,而字符流在操作时使用了缓冲区((缓冲区就是内存里的一块区域,把数据先存内存里,然后一次性写入,类似数据库的批量操作,这样效率比较高),通过缓冲区再操作

9.4 缓冲流

字节流和字符流上面进行套接

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rJ5eAujz-1631847537127)(/Users/yazhouheilong/Library/Application Support/typora-user-images/截屏2021-09-07 16.21.55.png)]

  • 为了提高数据读写速度,提供了带缓冲功能的流类,在使用这些流的时候,会创建一个内部缓存区数组,默认8kb

9.4.1 如何实现一行一行读取

public String readFile(){
       String path = "";
        File file = new File(path);
        StringBuilder result = new StringBuilder();
        try{
            BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(file), "UTF-8"));//构造一个BufferedReader类来读取文件

            String s = null;
            while((s = br.readLine())!=null){//使用readLine方法,一次读一行
                result.append( System.lineSeparator() + s);
            }
            br.close();
        }catch(Exception e){
            e.printStackTrace();
        }
        return result.toString();
 }

9.4.1 io流中的设计模式⭐️

Java I/O 使用了装饰者模式来实现。以 InputStream 为例,

  • InputStream 是抽象组件;
  • FileInputStream 是 InputStream 的子类,属于具体组件,提供了字节流的输入操作;
  • FilterInputStream 属于抽象装饰者,装饰者用于装饰组件,为组件提供额外的功能。例如 BufferedInputStream 为 FileInputStream 提供缓存的功能。

9.5 转换流

InputStreamReader:将一个字节的输入流转换为字符的输入流

OutputStreamWriter:将一个字符的输出流转换为字节的输出流

一般都是利用转换流解决乱码的问题

10.泛型

10.1 什么是泛型,泛型的好处是什么

泛型就是允许类或者接口在定义阶段进行参数的限制,或者是方法的返回值和参数的限制。它提供了编译期的类型安全,确保你只能把正确类型的对象放入集合中,避免了在运行时出现ClassCastException。

10.2 泛型擦除

如在代码中定义的List<Object>List<String>等类型,在编译之后都会变成List,JVM看到的只是List,而由泛型附加的类型信息对JVM来说是不可见的。