JAVA SE
Java特点
- 面向对象
- 平台无关
- 支持多线程
- 编译与解释并行
JVM、JDK、JRE
JVM: Java虚拟机,负责将Java字节码转换成特定平台的机器码并执行
JRE:Java运行时环境,包含运行Java程序所需的库,以及Java虚拟机
JDK:Java SDK,包含JRE以及编译器(javac)、Java文档生成工具(Javadoc)、Java调试器等开发工具。为开发者提供开发、编译、调试Java程序的一整套环境
跨平台性
指Java语言编写的程序,一次编译后,可以在多个系统平台上运行。
因为Java程序是通过JVM在系统平台上运行的,只要该系统可以安装相应的JVM,该系统就可以运行Java程序
字节码
Java程序经过编译之类产生的.class文件,字节码能够被虚拟机识别,从而实现Java程序的跨平台性
JAVA程序运行流程:
- 编译:将源码编译成虚拟机可以识别理解的字节码(.class)
- 解释:虚拟机执行Java字节码,将字节码翻译成机器码
- 执行:对应的机器执行二进制机器码
数据类型
- 基本数据类型
- 数值型
- 整数类型(byte、short、int、long)
- 浮点类型(float、double)
- 字符型(char)
- 布尔型(boolean)
| 数据类型 | 默认值 | 大小 |
|---|---|---|
| boolean | false | 1比特 |
| char | '\u0000' | 2字节 |
| byte | 0 | 1字节 |
| short | 0 | 2字节 |
| int | 0 | 4字节 |
| long | 0L | 8字节 |
| float | 0.0f | 4字节 |
| double | 0.0 | 8字节 |
- 引用数据类型
- 类 class
- 接口 interface
- 数组 []
类型转换
自动类型转换:把数范围小的数值或变量直接赋给另一个数范围大的变量
强制类型转换:自动类型转换反过来,会丢失精度
自动拆箱/装箱
&和&&有什么区别
&:逻辑与,两边都要计算
&&:短路与,若左边为false,则右边的表达式会直接短路掉,不会进行运算
tips: |和||也是如此
break、continue、return的区别及作用
- break跳出整个循环
- continue跳出这一次循环
- return程序返回
面向对象VS面向过程
- 面向过程:分析解决问题所需的步骤,然后用函数实现,使用的时候再一个一个的调用就可以
- 面向对象:把构成问题的事务分解成各个对象,建立对象是为了描述整个问题的过程所发生的行为,目的是复用代码
面向对象特性
-
封装:把一个对象的属性私有化,同时提供一些可以被外界访问的方法
-
继承:允许一个类继承现有类的属性和方法。提高代码复用性
-
多态:允许不同类的对象对同一消息做出响应,但表现出不同的行为
前置条件:
- 子类继承父类
- 子类重写父类的方法
- 父类引用指向子类对象
//子类继承父类
class Wangxiaoer extends Wanger {
public void write() { // 子类重写父类方法
System.out.println("记住仇恨,表明我们要奋发图强的心智");
}
public static void main(String[] args) {
// 父类引用指向子类对象
Wanger wanger = new Wangxiaoer();
wanger.write();
}
}
class Wanger {
public void write() {
System.out.println("勿忘国耻");
}
}
重载重写区别?
重载:一个类中有多个名字相同但是参数个数不同的方法
- 发生在一个类
- 同名方法有不同参数(参数类型,个数不同或者都不同)
重写:父类具有和子类一样的方法,用于提供父类已经声明的方法的特殊实现
- 发生在父类和子类之间
- 子类和父类的返回类型、方法名、参数列表相同
- 不能比父类的方法声明更多的异常
访问修饰符
抽象类和接口区别(is-a vs has-a)
- 一个类只能继承一个抽象类,但可以实现多个接口
- 抽象类可以有方法体的方法,但接口没有(Java8以前)
- 接口中的成员变量隐式为
static final,但抽象类不是 - 接口是隐式抽象,声明时没有必要使用
abstract关键字 - 接口中方法也是隐式抽象的,同样不需要。。。
- 接口中的方法都是隐式
public的
Java 抽象类和接口的区别,看这一篇就够了,全面解析 | 二哥的Java进阶之路 (javabetter.cn)
成员变量和局部变量的区别?
- 成员变量属于类,能被访问控制修饰符修饰;局部变量属于方法中定义的变量或方法参数
- 成员变量如果被static修饰,就属于类,没有则属于实例;
- 若局部变量为为基本数据类型,则存于栈内存;若为引用类型,则存于指向堆内存对象的引用或者是指向常量池中的地址
- 成员变量随对象的创建而存在,局部变量随方法的调用而自动消失
- 成员变量会被赋予初值,而局部变量不会
静态变量和实例变量区别?
- 静态变量:被static修饰的变量,属于类,不属于类中的任何一个对象,静态变量在内存中只有一个副本
- 实例变量:依存于实例,必须先创建对象然后通过对象才能访问
类似
- 静态方法:静态方法里不能访问类的非静态成员变量和方法
- 实例方法:。。。,可以访问类的所有成员变量和方法
final关键字
- 修饰类时不能被继承
- 修饰方法时,不能被重写
- 修饰变量时,一旦初始化就不能修改
- 基本数据类型数值初始化后不能更改
- 引用类型变量,初始化后不能让其指向另一个对象,但是引用指向的对象内容可以变
==和equals的区别
都用于比较对象
-
==:用于比较两个对象的引用,即它们是否指向同一个对象实例
对于基本数据类型,==比较的是值
-
euqals():用于比较两个对象的内容是否相等。默认
equals()方法和==相同,即比较对象引用
但是equals()方法经常被重写,如String类中,用于比较两个字符串的字符内容是否完全一样
String a = new String("hh");
String b = new String("hh");
// 使用 == 比较
System.out.println(a == b); // 输出 false,因为 a 和 b 引用不同的对象
// 使用 equals() 比较
System.out.println(a.equals(b)); // 输出 true,因为 a 和 b 的内容相同
hashCode和equals?
hashCode()用于获取哈希码,返回一个int整数,定义在Object类中,是一个本地方法。
哈希码由对象的内存地址或者对象属性计算出来,是int类型且不会重复,一般用作键值对的键
重写equals()方法必须重写hashCode()方法?
- 维护一致性
- 基于哈希的集合类通过对象的哈希码存储在不同的
桶中,查找对象时通过哈希码确定在哪个桶中搜索,然后通过equals()方法在桶中找到正确的对象 - 若重写equals而不重写hashCode,则被认为是相等对象可能会有不同的哈希码
为什么hashCode相同,对象也不一定相等?
哈希码通过哈希函数将对象映射成一个整数值
目的是在哈希表中快速定位对象的存储位置
哈希函数将一个较大的输入域映射到一个较小的输出域,不同输入值可能会产生相同的输出值(哈希冲突)
所以还得比较equals方法
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
e = p;
值传递
当一个对象作为参数传递到方法中,参数的值就是该对象的引用,引用的值是对象在堆中的地址
传递对象可以理解为传递变量存储的对象地址
深浅拷贝
-
浅拷贝:仅拷贝值,包括基本数据类型变量的值,和引用数据类型变量的地址值,不拷贝引用类型变量指向的堆中的对象
-
深拷贝:完全拷贝一份对象,更安全
浅拷贝实现?
通过Object类中的clone()方法
深拷贝实现?
- 重写克隆方法
- 序列化
创建对象方式
String
String是一个类,是引用数据类型。
且被final修饰,不可变不能被继承
String 和 StringBuilder、StringBuffer 的区别?
String不可变,用的多,每次修改都会产生新对象
StringBuilder常用于大量字符串连接,基于底层字符数组,不会产生新的String对象
StringBuffer线程安全,域StringBuilder类似
String str1 = new String("abc") 和 String str2 = "abc" 的区别?
Integer
对于第一对是相等的,会自动装箱,Integer.valueOf()来创建对象,该方法会缓存-128到127之间的Integer对象,因此,a和·b实际上引用了常量池中相同的Integer对象
Integer a = 127;
Integer b = 127;
String转Integer
- Integer.parseInt(String s)
- Integer.valueOf(String s)
这两种方法最终都会调用Integer类内的parseInt(String s,int radix)方法
异常处理
-
遇到异常不能进行具体处理,而是继续抛给调用者
throws用在方法上,后面跟的异常类,可以有多个
throw用在方法内,后面跟的异常对象
-
try catch捕获异常
finally语句块必然被执行
IO
按数据流分为
- 输入流:从源(文件、网络)读取数据到程序
- 输出流:将数据从程序写出到目的地(文件、网络、控制台)
按处理数据单位分为
- 字节流:以字节为单位读写数据,主要处理二进制数据,如音频、图像文件
- 字符流:以字符为单位进行读写,主要处理文本数据
按功能分为
- 节点流:直接与数据源或目的地相连,如 FileInputStream、FileOutputStream。
- 处理流:对一个已存在的流进行包装,如缓冲流 BufferedInputStream、BufferedOutputStream。
- 管道流:用于线程之间的数据传输,如 PipedInputStream、PipedOutputStream。
BIO、NIO、AIO区别
-
BIO:阻塞式I/O模型,线程在执行I/O操作时被阻塞,无法处理其他任务,适用于连接较少的场景
-
NIO:非阻塞I/O模型,线程在等待I/O时可执行其他任务,通过Selector监控多个Channel上的事件,适用于连接数多但连接时长短的场景
-
AIO:异步I/O模型,线程发起I/O请求后立即返回,当I/O操作完成时通过回调函数通知线程,
用于连接数多且连接时长长的场景
BIO简介
传统IO,基于字节流或字符流进行文件读写
对于每个连接,都需要创建一个独立的线程来处理读写
NIO简介
主要用于网络编程,服务器可以用一个线程处理多个客户端连接,通过 Selector 监听多个 Channel 来实现多路复用
AIO简介
使用I/O操作进行异步进行,线程发起一个读写操作后不必等待完成,可以进行其他任务,当读写操作真正完成,线程会被异步通知
反射
编译后生成字节码文件,JVM进行类加载的时候,会加载字节码文件,将类型相关的所有信息加载进方法区,反射就是去获取这些信息,然后操作
装来动态加载类并创建对象
String className = "java.util.Date";
Class<?> cls = Class.forName(className);
Object obj = cls.newInstance();
System.out.println(obj.getClass().getName());
访问字段和方法:
// 加载并实例化类
Class<?> cls = Class.forName("java.util.Date");
Object obj = cls.newInstance();
// 获取并调用方法
Method method = cls.getMethod("getTime");
Object result = method.invoke(obj);
System.out.println("Time: " + result);
// 访问字段
Field field = cls.getDeclaredField("fastTime");
field.setAccessible(true); // 对于私有字段需要这样做
System.out.println("fastTime: " + field.getLong(obj));