牛客 Java 1000 题 -- week01-day01
牛客 Java 1000 题 -- week01
day01
重载与重写
- 重载(Overload):同一个类,同名方法,不同参数
- 重写(Override):父子类,同名方法,相同参数,返回值相同或相容
- 在方法重写时,子类方法的返回值类型必须与父类方法的返回值类型相同,或者是其子类型(即相容类型)
- 相容类型:指那些可以相互赋值或转换的类型
- 子类与父类
- 接口的实现类
- 泛型通配符(?)
- 包装类和基本类型
- 相容类型:指那些可以相互赋值或转换的类型
- 在方法重写时,子类方法的返回值类型必须与父类方法的返回值类型相同,或者是其子类型(即相容类型)
Java源文件(.java)
Java源文件(.java)可以包含一个与文件名相同的public类和多个非public类
构造顺序
父类成员初始化 -> 父类构造方法 -> 子类成员初始化 -> 子类构造方法
- Java中子类构造方法会隐式调用父类的构造方法
- 成员变量的初始化在构造方法执行之前
静态变量
静态变量被所有对象共享,无论通过哪个对象访问,修改的都是同一个变量。
Java 中 == 和 equals
- ==:
- 引用类型:内存地址
- 基本类型:值
- equals:
Object
类中的方法- 对象所属的类没有重写 equals 方法,其功能等同于
==
- 重写之后,比较的是对象的值
- 对象所属的类没有重写 equals 方法,其功能等同于
成员内部类
修饰符 class 外部类名称{
修饰符 class 内部类名称{
// ..
}
// ..
}
内用外,随意访问;外用内,需要内部类对象
- 使用成员内部类:
外部类名称.内部类名称 对象名 = new 外部类名称().new 内部类名称();
- 如果出现了重名现象,访问外部类成员变量:
外部类名称.this.外部类成员变量名
局部内部类(包含匿名内部类)
局部内部类不能有修饰符
修饰符 class 外部类名称{
修饰符 返回值类型 外部类方法名称(参数列表){
class 局部内部类名称{
// ..
}
}
}
- 局部内部类是定义在方法或其他局部作用域内的类
- 如果希望访问所在方法的局部变量,那么这个局部变量必须是【有效 final 的】
- 因为局部内部类的生命周期可能会超过方法的调用期,为了保证数据一致性和安全性,要求被访问的局部变量不能被修改。
匿名内部类
接口名称 对象名 = new 接口名称(){
// 覆盖重写所有抽象方法
}
匿名内部类 & 匿名对象:都是只能使用唯一一次
- 匿名内部类:省略了【实现类 / 子类】
- 匿名对象:省略了【对象名称】
RuntimeException & Error
- 运行期异常可以被捕获和处理;
- Error 类代表了严重的问题,通常不应被捕获。
运算符 ^=
^=
:将左侧变量与右侧表达式进行异或运算后的结果再赋值给左侧变量。
Java 类加载器
- 引导类加载器(bootstrap class loader):负责加载核心 Java库;由 C语言编写,不继承自
java.lang.ClassLoader
- 扩展类加载器(extensions class loader):用来指明的目录中加载 Java的扩展库
- 系统类加载器(system class loader):也称 Apps类加载器。根据 Java应用程序的类路径(CLASSPATH)来加载 Java 类
Tomcat 默认情况下会自动为每个 Web 应用创建并配置一个 WebApp ClassLoader
WebApp ClassLoader
:为每个Web应用单独创建,加载该应用的类、JAR文件等
Java GC
-
Java中的内存回收是 JVM 通过垃圾回收(Garbage Collection,GC)机制自动处理的
-
垃圾回收的基本原理:
- 可达性分析
- 标记-清除(Mark-Sweep)
- 复制(Copying)
- 标记-整理(Mark-Compact)
- 分代收集(Generational Collection)
- Java 虚拟机将堆内存分为几个代(查看下面的 Java 内存区域)
-
垃圾回收器的类型
- Serial GC:单线程收集器
- Parallel GC:多线程收集器
- CMS (Concurrent Mark Sweep) GC:最小停顿时间收集器
- G1 GC:设计用来减少GC停顿时间的垃圾回收器
- ZGC 和 Shenandoah GC:低延迟垃圾回收器
-
手动触发垃圾回收(不建议手动触发垃圾回收,会影响 JVM 的自动管理机制)
System.gc(); // 请求JVM进行垃圾回收 Runtime.getRuntime().gc(); // 等价于System.gc()
Java 内存区域
- 堆内存(Heap)
- 新生代(Young Generation)
- Eden 区:新创建的对象首先分配在这里。
- Survivor 区:分为两个(From Space 和 To Space),在垃圾回收时,存活的对象会被复制到这两个区域之一。
- 老年代(Old Generation)或称为 Tenured Generation:存储经过多次垃圾回收仍然存活的对象。
- 永久代(PermGen):存储类的元数据,如类的结构信息、常量池等。JDK 8以后移除,被元空间 Metaspace 取代。
- 新生代(Young Generation)
- 元空间(Metaspace):元空间使用本地内存,而不是 JVM 堆内存。
- 栈内存(Stack):每个线程在创建时都会分配一个 Java 虚拟机栈。
- 程序计数器(PC Register):在虚拟机的概念模型里,字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令。
- 本地方法栈(Native Method Stacks):虚拟机栈为虚拟机执行 Java 方法服务,而本地方法栈则为虚拟机使用到的 Native 方法服务。
final 关键字 & effctively final
abstract
关键字和 final
关键字不能同时使用:二者相矛盾
-
修饰类(太监类):不能被继承
-
修饰方法:不能被重写
-
修饰局部变量:
- 基本类型:变量的数据不可变
- 引用类型:变量的地址值不可变
-
修饰成员变量:
- 由于成员变量具有默认值,所以用了 final 之后必须手动赋值,不会再给默认值了。
- 要么直接赋值
- 要么通过构造方法赋值
- 必须保证类当中所有的重载的构造方法,都最终会对 final 的成员变量进行赋值。
- 由于成员变量具有默认值,所以用了 final 之后必须手动赋值,不会再给默认值了。
-
effctively final:Java 8(是编译器自动推断的)
- 使用场景:
Lambda
表达式和匿名内部类
- 使用场景:
-
final vs effectively final:
特性 final effectively final 修饰符显式性 必须显式声明 隐式规则 (编译器自动推断) 适用范围 类、方法、局部变量、成员变量 仅适用于局部变量
& 和 &&
- &:非短路逻辑与,按位与 - 位远算符
- &&:短路逻辑与
子父类别称
- 父类:超类、基类
- 子类:派生类
Integer 对象的缓存机制
- 在Java中,Integer类型在-128到127之间的数值会被缓存。
- 当我们创建这个范围内的Integer对象时,实际上是从缓存池中取出对象,所以这些对象是同一个实例。
- 而超出这个范围的数值则会创建新的对象。
byte 是循环存储
- 取值范围:-128 ~ 127
- 当超出了byte的最大值127后,会从最小值-128重新开始
嵌入式系统 & 操作系统
嵌入式系统的软件可分为两大类:
- 无操作系统的嵌入式软件
- 带有操作系统的嵌入式软件
所以操作系统不是必须的
Arrays.asList()
- 作用:将数组转化成List集合。
- 返回的类型是:
java.util.Arrays$ArrayList
- 可用
List
接收:如List<String> list = Arrays.asList("a","b","c");
SimpleDateFormat 非线程安全
- SimpleDateFormat是非线程安全的。当多个线程同时使用同一个SimpleDateFormat对象时,可能会导致解析和格式化错误。
- 在实际开发中,建议为每个线程创建独立的SimpleDateFormat实例,或使用ThreadLocal来保证线程安全。
Java 7 和 Java 8 的 ConcurrentHashMap 线程安全实现
- Java 7 采用 【
Segment
】 分段锁来保证安全, Segment 是继承自 ReentrantLock。 - Java 8 放弃了 Segment 分段锁的设计,采用 【Node + CAS +
synchronized
】 保证线程安全,锁粒度更细,synchronized 只锁定当前链表或红黑二叉树的首节点。
JSON
轻量级的数据交换格式:是基于 ECMAScript 的一个自己设计的,是一种开放标准的文件格式和数据交换格式。
-
对象
{ "name": "Alice", "age": 25, "isStudent": false }
-
数组
[ "Apple", "Banana", "Cherry" ]
ThreadLocal
java.lang.ThreadLocal
-
当线程调用ThreadLocal的
set()
或get()
方法时,实际上是在操作当前线程的ThreadLocalMap实例。 -
ThreadLocalMap使用一个
Entry
数组来存储键值对,其中键为ThreadLocal对象,值为用户定义的对象。 -
尽可能避免使用静态
ThreadLocal
变量,除非你确保每个使用它的线程最终会清理它。- 容易造成内存泄漏:
- 静态
ThreadLocal
变量:如果你有一个静态的ThreadLocal
变量,它将不会被垃圾回收,因为它属于类而不是任何实例。如果一个线程的生命周期长于应用程序的生命周期,那么这个线程的ThreadLocalMap
中的条目将永远不会被清理。 - 线程长时间运行:如果一个线程的生命周期很长,并且在长时间运行期间不断使用
ThreadLocal
变量而不清理(例如,通过调用remove()
方法),这将导致内存占用不断增加。
- 静态
- 容易造成内存泄漏:
应用实例:使用ThreadLocal
来存储SimpleDateFormat
对象
避免在多线程环境下使用同一个SimpleDateFormat
实例可能引起的线程安全问题
- 使用方法:
static <S> ThreadLocal<S> withInitial(Supplier<? extends S> supplier)
:Creates a thread local variable.
public class ThreadLocalSimpleDateFormat {
// 创建一个ThreadLocal对象,用于存储SimpleDateFormat实例
private static final ThreadLocal<SimpleDateFormat> dateFormatThreadLocal = ThreadLocal.withInitial(() -> {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
// 设置时区为UTC,避免时区问题
sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
return sdf;
});
public static String formatDate(Date date) {
// 从ThreadLocal获取SimpleDateFormat实例
SimpleDateFormat sdf = dateFormatThreadLocal.get();
// 使用SimpleDateFormat格式化日期
return sdf.format(date);
}
public static void main(String[] args) {
Date date = new Date();
System.out.println(formatDate(date)); // 输出格式化后的日期字符串
}
}
补充
Java 内部始终使用 Unicode 存储字符,编码方式只影响源文件的读取。