1、jvm 类加载器 是运行Java字节码的虚拟机 字节码并不针对某一种操作系统
JRE 运行Java程序 (jvm+一些基础类库+其他一些构件)
JDK 创建和编译程序 (javac+工具)
2、Java为什么是编译和解释并存 下图为Java程序源代码到运行的过程
什么叫编译 一次编译成机器可理解的代码 Java编译过程将Java源代码编译为与平台无关的字节码,这种字节码可以在任何支持JVM的环境中执行。
解释 一句一句解释成机器可理解的代码
Java是编译和解释的两个地方 一是javac编译 解释器执行字节码文件 还有就是热点代码通过JIT编译器进行编译
3、基本数据类型 其中float是4个字节 bollean是一位 字符型常量是占两个字节 double是8个字节
基本类型和包装类型的区别 用途(对象、泛型、集合) 存储方式(堆(局部变量、成员变量) 栈)默认值是null =比的不同
包装类缓存机制 Byte
,Short
,Integer
,Long
这 4 种包装类默认创建了数值 [-128,127] 的相应类型的缓存数据,Character
创建了数值在 [0,127] 范围的缓存数据,Boolean
直接返回 True
or False。浮点数没有缓存机制
什么是自动拆装箱?
- 装箱:将基本类型用它们对应的引用类型包装起来 ;Integer i = Integer.valueOf(10)
- 拆箱:将包装类型转换为基本数据类型;
int n = i.intValue()
;
为什么要有包装类?
包装类提供了基本数据类型所不具备的对象化特性和丰富的方法支持,使得它们能够在更广泛的场景中使用。这些类的设计使得 Java 在处理基本数据类型时既能保持高效性,又能享受面向对象编程的便利。
存在问题
空指针异常,因为integer可以存null值,这时候用自动拆箱的int接收就会出错
比如数据库中读取数据、比如集合操作,对应的Map中的键也有可能是null
4、成员变量和局部变量的区别 成员变量如果没有赋值 默认赋值 涉及知识——内存初始化
类 方法 ; 堆 栈 ; 对象的存亡 方法的调用
静态变量 是所有实例的 通过类名来访问 节省内存
5、静态方法和实例方法有什么不同 调用方法的不同 访问类成员是否存在限制(静态方法不能访问非静态成员 )实例方法没有这个限制
重写和重载的区别
重写要遵守 两同(方法名、形参列表)两小(返回值类型比父类小,剖出的异常比父类要小),一大 (子类的访问权限要更大)
重载就是同一个类中多个同名方法根据不同的传参来执行不同的逻辑处理 法名必须相同,参数类型不同、个数不同、顺序不同,方法返回值和访问修饰符可以不同。
遇到方法重载的情况怎么办呢?会优先匹配固定参数还是可变参数的方法呢? 优先匹配固定参数的方法,因为固定参数的方法匹配度更高。
6、什么是面向对象?
面向对象编程是一种通过创建和操作对象来设计和构建软件系统的编程范式。它通过类和对象来组织代码,通过封装、继承、多态和抽象等特性来提高代码的可维护性、可重用性和可扩展性。通过OOP,可以更自然地映射现实世界的问题,并以模块化的方式进行编程,提升软件开发的效率和质量
对象引用保存的是对象实例的内存地址
继承 旨在提高代码的重用性和开发效率。通过继承,子类可以复用父类中定义的属性和方法,同时还可以添加自己的属性和方法。
子类继承了父类的所有属性和方法,但不能直接访问父类中的私有属性和私有方法。为了让子类能够访问这些私有成员,父类可以提供公共(public)或受保护(protected)的方法来间接访问它们。
多态 允许同一个接口(或父类)调用在不同的实例上表现出不同的行为
7、抽象类和接口有什么区别呢?
- 抽象类:提供类的部分实现,可以包含成员变量、构造方法和具体方法。适用于一组相关类的公共行为和属性。 当你有一组相关的类,且希望共享一些代码(方法实现或成员变量)时,使用抽象类
- 接口:定义类应该实现的一组方法,不能包含实例变量(只能有静态常量)。适用于定义不相关类的公共行为,可以实现多继承。 当你需要定义一组不相关类的公共行为时,使用接口
8、深拷贝和浅拷贝
- 浅拷贝会在堆上创建一个新的对象 拷贝对象和原对象共用同一个内部对象。
- 深拷贝会完全复制整个对象,包括这个对象所包含的内部对象。
-
- hashcode哈希码 确定对象在哈希表中的索引位置 哈希算法也会刚好让多个对象传回相同的哈希值。
- string a=new string(“b”)会在字符串池中存储一份字面量 如果字符串常量池中不存在字符串对象“b”的引用,那么它会在堆上创建两个字符串对象,其中一个字符串对象的引用会被保存在字符串常量池中。
String
中的equals
方法是被重写过的,比较的是 String 字符串的值是否相等。Object
的equals
方法是比较的对象的内存地址- string.intern是将指定的字符串对象的引用保存在字符串常量池中
- 常量折叠 编译器在程序编译期就可以确定值的常量
9、Java异常
error(outofmemoryerror 系统级的异常,通常程序无法恢复 这些异常发生时,Java 虚拟机(JVM)一般会选择线程终止) exception(程序本身可以处理的异常,可以通过 catch
来进行捕获。 受检查 编译器能够检查到的 classnotfoundexception 不受检查异常 编译器检查不到的 NullPointerException
(空指针错误)
10、泛型 构建集合list<T> ;自定义返回结果类型;泛型方法、泛型接口、泛型类
泛型擦除机制 在编译过程中,Java 编译器会将泛型类型替换为实际的类型参数或其边界(如果没有指定边界,则替换为 Object
),并移除所有的泛型信息。因此,在运行时,Java 程序实际上看不到泛型的类型信息 比如说 List<String> list = new ArrayList<>();
这段代码在编译时确实会由于泛型的类型擦除而被编译成**List list = new ArrayList<>();
**。
泛型使得编译器可以在编译的时候检查类型安全问题,List<String>,使得代码能够复用
泛型限制
不能用instanceof检查
无法使用基本数据类型作为泛型类型参数
无法实例化泛型类型的对象
无法在静态上下文中使用泛型类型参数
上界通配符 下界通配符 无界通配符如( ?
、? extends T
、? super T
)
11、序列化 将对象转换成二进制字节流的过程 存储到文件系统、数据库、内存中
不想进行序列化的变量,使用 transient
关键字修饰
static
变量因为不属于任何对象(Object),所以无论有没有 transient
关键字修饰,均不会被序列化
implements Serializable
private static final long serialVersionUID = 1905122041950251207L;
serialVersionUID
只是用来被 JVM 识别,实际并没有被序列化。
12、Java中的值传递
基本数据类型:传递的是值的副本,方法内部的修改不影响方法外部的原始数据。
对象引用:传递的是引用的副本,方法内部通过引用修改对象的内容会影响方法外部的对象,但不能改变引用本身的指向。
13、Java集合 大小可变 支持泛型 存储对象 高质量代码编写
arraylist底层是数组 linkedlist的底层是双向链表 所以arraylist是索引,可以快速访问,实现randomaccess接口 但是linkedlist是链表,是靠指针定位的,索引不能实现快速访问 数组可以快速定位, 但是增删需要移动 链表不能快速定位,需要查找,增删的时候很快
arraylist扩容机制
以无参数构造方法创建 ArrayList
时,实际上初始化赋值的是一个空数组。当真正对数组进行添加元素操作时,才真正分配容量。即向数组中添加第一个元素时,数组容量扩为 10。当容量不足时,ArrayList
会扩展内部数组以确保能够容纳新元素。 1.5倍的扩容
hashset是通过hashcode值计算它存放的位置 底层是哈希表
treeset的底层是红黑树
hashmap的底层是 数组和链表结合在也一起的链表散列
concurrenthashmap是
node加锁保证链表存储的安全 TreeNode
是存储红黑树节点,被TreeBin
包装。TreeBin
通过waiter
属性维护当前使用这棵红黑树的线程,来防止其他线程的进入。它的冲突再达到一定大小时会转化成红黑树,在冲突小于一定数量时又退回链表。
14、arraycopy()
需要目标数组,将原数组拷贝到你自己定义的数组里或者原数组,而且可以选择拷贝的起点和长度以及放入新数组中的位置 copyOf()
是系统自动在内部新建一个数组,并返回该数组
15、IO
常用字符编码所占字节数?utf8
:英文占 1 字节,中文占 3 字节,unicode
:任何字符都占 2 个字节,gbk
:英文占 1 字节,中文占 2 字节。
字节流 字节数组
InputStream fis = new FileInputStream("F:\\MjExe\\mjIO\\src\\a.txt");
OutputStream fos = new FileOutputStream("F:\\MjExe\\mjIO\\src\\c.txt");
int len = 0;
byte[] bytes=new byte[1024];
while ((len = fis.read(bytes)) != -1) {
fos.write(bytes,0,len);
}
fis.close();
fos.close();
适配器模式 让接口不兼容而不能交互的类可以一起工作 不需要继承相同的抽象类或者实现相同的接口。
InputStreamReader isr=new InputStreamReader(new FileInputStream("F:\\MjExe\\mjIO\\src\\a.txt"));
OutputStreamWriter osw=new OutputStreamWriter(new FileOutputStream("F:\\MjExe\\mjIO\\src\\c.txt"));
int len = 0;
char[] bytes=new char[1024];
while ((len = isr.read(bytes)) != -1) {
osw.write(bytes,0,len);
}
isr.close();
osw.close();
字符流 字符数组
装饰器模式 不改变原有对象的情况下拓展其功能 装饰器模式通过组合替代继承来扩展原始类的功能,装饰器类需要跟原始类继承相同的抽象类或者实现相同的接口 在一些继承关系比较复杂的场景(IO 这一场景各种类的继承关系就比较复杂)更加实用。
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("F:\\MjExe\\mjIO\\src\\a.txt"));
BufferedOutputStream bos=new BufferedOutputStream(new FileOutputStream("F:\\MjExe\\mjIO\\src\\c.txt"));
int len = 0;
byte[] bytes=new byte[1024];
while ((len = bis.read(bytes)) != -1) {
bos.write(bytes,0,len);
}
bis.close();
bos.close();
16、String、StringBuffer 和 StringBuilder 的区别是什么? String 为什么是不可变的?
所有的String,默认都是以常量形式保存,且由final修饰 ,所以是不可变的
StringBuffer 用了synchronize关键字