二本26届大三Java实习准备 Day1
2025 年 2 月 24 日 Day 1
今日算法题 ******** ******** **************
今日复习:
- java 基础
语法基础
Java 的数据类型分类
java 的数据类型可以粗略的分为两大类: 基本数据类型和引用数据类型 基本数据类型有 int char float boolean 等类型 引用数据类型包括类、数组、接口等
char 类型占两个字节因为采用的是 unicode 编码
String 为什么不可变
从底层代码的层面来说; String 的底层代码是一个是被 final 修饰的 char 数组; 因为被 fianl 修饰意味着他是不可变的; 而设计成不可变的有以下一些好处:
- 性能提高: 因为不可变存储在 jvm 的常量池中, 那么对使用频率高的字符串来说使用性能就很高相当于天然的 jvm 层的缓存
- 安全性高: 因为他的不可变性, 当我们在 java 的某个位置处理某些铭感字符串时可以确保他在传递过程中不被修改
- 线程安全: 因为是不变的, 那么在多线程共享同一个字符串常量池的时候就不用考虑因为并发而引起的诸多问题
- Hash 缓存: 因为字符合经常参与 hash 运算, 不变的字符串可以只进行第一次 hash 运算,后续相同的字符串就可以不需要进行重复运算
String, StringBuilder, StringBuffer 三者的区别和关系
String 是不可变的, 而且其他两个是可变的; 不可变意味着如何在字符串运算的时候 String 每次都要创建新的常量对象, 一次简单的操作就可能创建许多中间常量对象, 这样的性能十分低下资源消耗也很大; 所以 StringBuilder 和 StringBuffer 就孕育而生了; StringBuilder 的可变性就意味着他不具备 String 的不可变优点, 比如 Hash 缓存,线程安全; 为了解决线程安全问题就诞生了 StringBuffer 因为 StringBuffer 是线程安全的且他的底层是通过加锁来实现的, 所以他的性能会比 StringBuilder 低下
new String 的时候创建的是几个对象
答案是 1 个或者两个,答案取决于所创建的字符串是否已经在 jvm 字符串常量池中存在; 如果在 new 执行之前字符串已经存在那么就只会在栈上创建一个对象然后指向常量池中的字符串常量; 如果常量池中没有那么就会将字符串放到常量池中然后再在栈上创建一个对象指向这个常量
字符串拼接是如何实现的
String 的所有运算操作都会转为 StringBuilder 然后进行操作; 所以在大量的 String 运算操作情况下一般直接使用 StringBuilder 或者 StringBuffer 避免转换的开销
接口和抽象的区别
接口和抽象都不能直接实例化, 但是他们的作用不同所以他们有以下不同点:
- 修饰符接口只能被 public 修饰, 而抽象类没有限制
- 子类上对接口的关键字是 implement,而抽象类是 extends; java 为了避免菱形继承造成歧义不支持多继承以为只一个类继承一个抽象父类, 但是可以同时实现多个接口
- 接口没有构造方法, 抽象类可以有抽象方法; 虽然两者都不能实例化但是抽象类可以指定抽象方法这样子类实现实例化的过程中就可以触发抽象类的构造方法
- 最后也是本质的区别: 接口的诞生是由上至下的, 而抽象类是由下至上的; 接口是目的是制定规范化, 而抽象类是提取类的共同方法是属性目的是提高复用性
@PostConstruct、init-method 和 afterPropertiesSet 执行顺序
- 静态代码块
- 类被加载时执行一次
- 构造方法
- @PostConstruct
- afterPropertiesSet
- init-method
对象的创建过程
对象创建的本质就是 jvm 将类加载到内存中并非配空间然后填充属性和执行初始化操作;
- 判断类是否已经被加载到 JVM 中了, 如果没有则通过类加载器将类加载到内存中
- 当类被加载后就能确定一个实例对象应该占多大的内存空间, 然后就进行内存空间的分配
- 空间分配完成之后 JVM 层面的认为就完成了, 然后 java 就开始为对象的字段进行赋零值操作, 比如为 int 赋值 0, 为引用数据类型赋值 null
- 然后设置对象头中的信息, 比如锁、垃圾回收 GC 、hash 码等其他信息
- 最后执行执行 init 方法进行初始化
深拷贝和浅拷贝
浅拷贝指的是将引用地址复制给对象, 而深拷贝则是递归创建一个全新的独立对象进行复制给对象; 浅拷贝的某个对象对引用对象进行了修改那么就会影响到所有副本, 而深拷贝则是完全独立的; 可以简单的和函数的值传递和地址传递进行比较理解
强软弱虚引用的区别
强软弱虚是根据他们的回收优先级来划分的, 简单来说就说越强的引用越不容易被垃圾回收
- 强引用: 指的说可以通过直达算法 CG Root 直接找到的元素, 只要依然可达就不会被释放
- 软引用: 经过多次 GC 依然内存不出时进行回收
- 弱引用: 当进行 GC 时就会被释放的引用
- 虚引用: 配合延迟队列使用, 一般实现对象释放是的通知机制, 可以在对象释放前进行灵活的处理
==和 equal 的区别
对于基本数据类型来说· ==比较的是两个对象的值; 而对于引用数据来说比较的是两个对象的地址; equal 是定义在 Object 类中的方法, 所以所有类都可以重写他, 一般重写 equal 的时候都会重写 hashCode 方法
为什么重写 equal 时候需要重写 hashCode
重写 equal 时必须重写 hashCode 方法是为了避免出现逻辑不一致的问题从而导致无法和 Hash 函数进行配合使用; 因为 hash 规定同一个对象进过 hash 之后的值应该是相同的; 如果我们只是重写了 equal 方法但是确没有重写 hashCode 方法就有可能出现a.equal (b)返回 ture, 意味着两个对象是相等的但是他们 hashCode 却不一样, 也就会导致同一个对象被散列到了多个不同的位置; 为了避免这种严重的错误我们规定 equal 和 hashCode 一起进行重写
final, finally, finallize 的分别作用
fianl 是修饰符;
- final 用于修饰类、方法和变量;
- 修饰类表示该类不能再被继承
- 修饰方法表示该方法不能再被子类重写
- 修饰变量表示为常量;
finally 是关键字;
- finallly 配合 try 使用一般用于释放资源 finallize 是方法;
- 这是一个钩子方法重写这个方法可以指定对象被回收时所需要执行的操作
四种访问修饰符
- private(私有):只能在同一个类中访问
- protected(受保护):对同一个包内的类和所有子类可见
- default(默认):在同一个包内可见
- public(公共):对所有类可见
三大面向对象特性
面向对象三大特性: 封装、继承、多态 封装:
- 封装是只对类中的数据和方法进行保护, 开放一部分特定的接口供外部访问; 提高了程序的安全性 继承:
- 继承是只一个子类继承父类, 继承的出现大大的提高了代码的复用性;
- 继承的出现为多态提供了基础+ 多态:
- 多态从使用层面来说就是父类的引用指向子类的对象;
- 多态的出现一定程度上实现了对同一个消息不同子类会进行不同的响应
重写和重载的区别
重写只的是在接口实现和抽象类继承的场景中; 他一般是完成接口和抽象类指定的方法或者是父类的方法无法满足当前类的功能, 需要对父类的方法进行增强的场景中; 而重载是指一个类中一个同名函数的多个实现, 这些实现方法的函数名相同但是参数列表和返回值不同, 实现了在不同参数情况下调用不同同名方法的效果
什么是泛型?在 Java 中有什么作用?
#java##面试#泛型用于在定义类和接口时定义类型参数; 可以简单理解为类的变量; 运行时用具体的类型进行替代; 好处:
- 泛型将部分类型检测提前到了编译阶段可以避免部分运行时类型异常;
- 使用泛型可以省略许多手动的类型转换, 比如从一个 List 中取出一个对象后需要手段转换为 String 类型; 而从一个
List<String>
中取出一个对象则不需要转换操作- 提高代码的复用性, 不需要为不同的类型单独编写代码, 而是通过泛型进行占位替代
泛型的原理就是类型擦除, 简单来说就是在. java 文件变以为字节码文件. class 过程中将泛型擦除; 所以泛型只在编译时期进行类型检查, 并且在 JVM 层面感知不到泛型
泛型的通配符 : ?表示任意类型 extends T 表示 T 及其子类 supper T 表示 T 及其父类