- 基本类型(拆装箱)
- 关键字
- 泛型(Java中的泛型是伪泛型, 编译时泛型会被擦除)
- == 和 equals
- 在一个静态方法内调用一个非静态成员为什么是非法的?
- 静态方法和实例方法有何不同?(最终还是因为以上加载的原因)
- 重载 与 重写
- 浅拷贝 和 深拷贝
- 成员变量 和 局部变量
- 1. 从语法形式上看,成员变量是属于类的,而局部变量是在代码块或方法中定义的变量或是方法的参数;成员变量可以被 访问修饰符,
static
等修饰符所修饰,而局部变量不能被访问控制修饰符及static
所修饰;但是,成员变量和局部变量都能被final
所修饰。 - 2. 从变量在内存中的存储方式来看,如果成员变量是使用
static
修饰的,那么这个成员变量是属于类的,如果没有使用static
修饰,这个成员变量是属于实例的。而对象存在于堆内存,局部变量则存在于栈内存。 - 3. 从变量在内存中的生存时间上看,成员变量是对象的一部分,它随着对象的创建而存在,而局部变量随着方法的调用而自动消失。
- 4. 从变量是否有默认值来看,成员变量如果没有被赋初,则会自动以类型的默认值而赋值(一种情况例外:被
final
修饰的成员变量也必须显式地赋值),而局部变量则不会自动赋值。
- 1. 从语法形式上看,成员变量是属于类的,而局部变量是在代码块或方法中定义的变量或是方法的参数;成员变量可以被 访问修饰符,
- 面向对象的三大特征
- String, StringBuffer, StringBuilder
- Object类的方法总结:
- 反射">反射 反射
- 异常
- final,static,this,super 关键字
- final
- 1. final 修饰的类不能被继承,final 类中的所有成员方法都会被隐式的指定为 final 方法;
- static
- 修饰成员变量和成员方法: 被 static 修饰的成员属于类,不属于单个这个类的某个对象,被类中所有对象共享,可以并且建议通过类名调用。被 static 声明的成员变量属于静态成员变量,静态变量 存放在 Java 内存区域的方法区。调用格式:
类名.静态变量名
类名.静态方法名()
- 静态代码块: 静态代码块定义在类中方法外, 静态代码块在非静态代码块之前执行(静态代码块—>非静态代码块—>构造方法)。 该类不管创建多少对象,静态代码块只执行一次.
- 静态内部类(static 修饰类的话只能修饰内部类): 静态内部类与非静态内部类之间存在一个最大的区别: 非静态内部类在编译完成之后会隐含地保存着一个引用,该引用是指向创建它的外围类,但是静态内部类却没有。没有这个引用就意味着:1. 它的创建是不需要依赖外围类的创建。2. 它不能使用任何外围类的非 static 成员变量和方法。
- 静态导包(用来导入类中的静态资源,1.5 之后的新特性): 格式为:
import static
这两个关键字连用可以指定导入某个类中的指定静态资源,并且不需要使用类名调用类中静态成员,可以直接使用类中静态成员变量和成员方法。
- 修饰成员变量和成员方法: 被 static 修饰的成员属于类,不属于单个这个类的某个对象,被类中所有对象共享,可以并且建议通过类名调用。被 static 声明的成员变量属于静态成员变量,静态变量 存放在 Java 内存区域的方法区。调用格式:
- this 用于引用类的当前实例
- super 用于从子类访问父类的变量和方法
- 使用 this 和 super 要注意的问题:
基本类型(拆装箱)
基本类型 | 位数 | 字节 | 默认值 |
---|---|---|---|
int |
32 | 4 | 0 |
short |
16 | 2 | 0 |
long |
64 | 8 | 0L |
byte |
8 | 1 | 0 |
char |
16 | 2 | ‘u0000’ |
float |
32 | 4 | 0f |
double |
64 | 8 | 0d |
boolean |
1 | 1/8 | false |
包装类(Integer, Short, Long等, 不赋值的话, 默认值救是 null)
基本类型的包装类和常量池
Byte,Short,Integer,Long [-128,127]
Character [0,127]
Boolean true, false
Integer i1 = 40; // i1 直接使用的是常量池中的对象
Integer i2 = new Integer(40); // 会直接创建新的对象。
System.out.println(i1==i2); // false
// Integer i1=40 这一行代码会发生装箱,也就是说这行代码等价于
// Integer i1=Integer.valueOf(40) 。
// 因此,i1 直接使用的是常量池中的对象。而Integer i1 = new Integer(40) 会直接创建新的对象。
Integer i1 = 40;
Integer i2 = 40;
System.out.println(i1 == i2); // true, 在-128-127之内
Integer i1 = 128;
Integer i2 = 128;
System.out.println(i1 == i2); // false, 在-128-127之外
关键字
访问控制 | private | protected | public | ||||
---|---|---|---|---|---|---|---|
类,方法和变量修饰符 | abstract | class | extends | final | implements | interface | native |
new | static | strictfp | synchronized | transient | volatile | ||
程序控制 | break | continue | return | do | while | if | else |
for | instanceof | switch | case | default | |||
错误处理 | try | catch | throw | throws | finally | ||
包相关 | import | package | |||||
基本类型 | boolean | byte | char | double | float | int | long |
short | null | true | false | ||||
变量引用 | super | this | void | ||||
保留字 | goto | const |
泛型(Java中的泛型是伪泛型, 编译时泛型会被擦除)
== 和 equals
1. 对于基本数据类型来说,==比较的是值; 对于引用数据类型来说,==比较的是对象的内存地址
2. equals()
作用不能用于判断基本数据类型的变量,只能用来判断两个对象是否相等
equals容易抛出空指针, 推荐使用 Object.equals(Object o1, Object o2);
Object.equals的源码:
public static boolean equals(Object a, Object b) {
// 可以避免空指针异常。如果a==null的话此时a.equals(b)就不会得到执行,避免出现空指针异常。
return (a == b) || (a != null && a.equals(b));
}
== 和 euqals 的实例
public class test1 {
public static void main(String[] args) {
String a = new String("ab"); // a 为一个引用
String b = new String("ab"); // b为另一个引用,对象的内容一样
String aa = "ab"; // 放在常量池中
String bb = "ab"; // 从常量池中查找
if (aa == bb) // true
System.out.println("aa==bb");
if (a == b) // false,非同一对象
System.out.println("a==b");
if (a.equals(b)) // true
System.out.println("aEQb");
if (42 == 42.0) { // true
System.out.println("true");
}
}
}
// 当创建 String 类型的对象时,虚拟机会在常量池中查找有没有已经存在的值和要创建的值相同的对象,
// 如果有就把它赋给当前引用。如果没有就在常量池中重新创建一个 String 对象
在一个静态方法内调用一个非静态成员为什么是非法的?
这个需要结合 JVM 的相关知识,静态方法是属于类的,在类加载的时候就会分配内存,可以通过类名直接访问。而非静态成员属于实例对象,只有在对象实例化之后才存在,然后通过类的实例对象去访问。在类的非静态成员不存在的时候静态成员就已经存在了,此时调用在内存中还不存在的非静态成员,属于非法操作。
静态方法和实例方法有何不同?(最终还是因为以上加载的原因)
1. 调用方式: 在外部调用静态方法时,可以使用”类名.方法名”的方式,也可以使用”对象名.方法名”的方式。而实例方法只有后面这种方式。也就是说,调用静态方法可以无需创建对象。
2. 可访问的成员: 静态方法在访问本类的成员时,只允许访问静态成员(即静态成员变量和静态方法),而不允许访问实例成员变量和实例方法;实例方法则无此限制。
重载 与 重写
区别点 | 重载方法 | 重写方法 |
---|---|---|
发生范围 | 同一个类 | 子类 |
参数列表 | 必须修改 | 一定不能修改 |
返回类型 | 可修改 | 子类方法返回值类型应比父类方法返回值类型更小或相等 |
异常 | 可修改 | 子类方法声明抛出的异常类应比父类方法声明抛出的异常类更小或相等; |
访问修饰符 | 可修改 | 一定不能做更严格的限制(可以降低限制) |
发生阶段 | 编译期 | 运行期 |
方法的重写要遵循“两同两小一大”:
“两同”即方法名相同、形参列表相同;
“两小”指的是子类方法返回值类型应比父类方法返回值类型更小或相等,子类方法声明抛出的异常类应比父类方法声明抛出的异常类更小或相等;
“一大”指的是子类方法的访问权限应比父类方法的访问权限更大或相等。
浅拷贝 和 深拷贝
浅拷贝:对基本数据类型进行值传递,对引用数据类型进行引用传递般的拷贝,此为浅拷贝。
深拷贝:对基本数据类型进行值传递,对引用数据类型,创建一个新的对象,并复制其内容,此为深拷贝。
成员变量 和 局部变量
1. 从语法形式上看,成员变量是属于类的,而局部变量是在代码块或方法中定义的变量或是方法的参数;成员变量可以被 访问修饰符, static
等修饰符所修饰,而局部变量不能被访问控制修饰符及 static
所修饰;但是,成员变量和局部变量都能被 final
所修饰。
2. 从变量在内存中的存储方式来看,如果成员变量是使用 static
修饰的,那么这个成员变量是属于类的,如果没有使用 static
修饰,这个成员变量是属于实例的。而对象存在于堆内存,局部变量则存在于栈内存。
3. 从变量在内存中的生存时间上看,成员变量是对象的一部分,它随着对象的创建而存在,而局部变量随着方法的调用而自动消失。
4. 从变量是否有默认值来看,成员变量如果没有被赋初,则会自动以类型的默认值而赋值(一种情况例外:被 final
修饰的成员变量也必须显式地赋值),而局部变量则不会自动赋值。
面向对象的三大特征
封装
继承
1. 子类拥有父类对象所有的属性和方法(包括私有属性和私有方法),但是父类中的私有属性和方法子类是无法访问,只是拥有。
2. 子类可以拥有自己属性和方法,即子类可以对父类进行扩展。
3. 子类可以用自己的方式实现父类的方法。
多态
String, StringBuffer, StringBuilder
可变性:
1. String: 使用 final 关键字修饰字符数组来保存字符串, private final char value[]
,所以String
对象是不可变的。
2. StringBuilder
与 StringBuffer
都继承自 AbstractStringBuilder
类,在 AbstractStringBuilder
中也是使用字符数组保存字符串char[]value
但是没有用 final
关键字修饰,所以这两种对象都是可变的。
故: String不可变, StringBuffer 和 StringBuilder 可变
在 Java 9 之后,String 、StringBuilder
与 StringBuffer
的实现改用 byte 数组存储字符串 private final ``byte[]`` value
线程安全性:
1. String
中的对象是不可变的,也就可以理解为常量,线程安全
2. StringBuffer 和 StringBuilder 的公共父类 —> AbstractStringBuilder 定义了一些字符串的基本操作,如 expandCapacity
、append
、insert
、indexOf
等公共方法. 但是, StringBuffer
对方法加了同步锁或者对调用的方法加了同步锁,所以是线程安全的. StringBuilder
并没有对方法进行加同步锁,所以是非线程安全的。
性能: StringBuilder(有线程风险) > StringBuffer > String
总结:
1. 操作少量的数据: 适用 String
2. 单线程操作字符串缓冲区下操作大量数据: 适用 StringBuilder
3. 多线程操作字符串缓冲区下操作大量数据: 适用 StringBuffer
Object类的方法总结:
Object 类是一个特殊的类,是所有类的父类。它主要提供了以下 11 个方法:
public final native Class<?> getClass() //native方法,用于返回当前运行时对象的Class对象,使用了final关键字修饰,故不允许子类重写。
public native int hashCode() //native方法,用于返回对象的哈希码,主要使用在哈希表中,比如JDK中的HashMap。
public boolean equals(Object obj) //用于比较2个对象的内存地址是否相等,String类对该方法进行了重写用于比较字符串的值是否相等。
protected native Object clone() throws CloneNotSupportedException
// naitive方法,用于创建并返回当前对象的一份拷贝。
// 一般情况下,对于任何对象 x,表达式 x.clone() != x 为true,x.clone().getClass() == x.getClass() 为true。Object本身没有实现Cloneable接口,
// 所以不重写clone方法并且进行调用的话会发生CloneNotSupportedException异常。
public String toString() //返回类的名字@实例的哈希码的16进制的字符串。建议Object所有的子类都重写这个方法。
public final native void notify() //native方法,并且不能重写。唤醒一个在此对象监视器上等待的线程(监视器相当于就是锁的概念)。如果有多个线程在等待只会任意唤醒一个。
public final native void notifyAll() //native方法,并且不能重写。跟notify一样,唯一的区别就是会唤醒在此对象监视器上等待的所有线程,而不是一个线程。
public final native void wait(long timeout) throws InterruptedException
//native方法,并且不能重写。暂停线程的执行。注意:sleep方法没有释放锁,而wait方法释放了锁 。timeout是等待时间。
public final void wait(long timeout, int nanos) throws InterruptedException
//多了nanos参数,这个参数表示额外时间(以毫微秒为单位,范围是 0-999999)。 所以超时的时间还需要加上nanos毫秒。
public final void wait() throws InterruptedException //跟之前的2个wait方法一样,只不过该方法一直等待,没有超时时间这个概念
protected void finalize() throws Throwable { } //实例被垃圾回收器回收的时候触发的操作
反射 反射
异常
Java 异常类层次结构图
Exception 和 Error:
**Exception**
:程序本身可以处理的异常,可以通过 catch
来进行捕获。Exception
又可以分为 受检查异常(必须处理) 和 不受检查异常(可以不处理)。
**Error**
:Error
属于程序无法处理的错误 ,我们没办法通过 catch
来进行捕获 。例如,Java 虚拟机运行错误(Virtual MachineError
)、虚拟机内存不够错误(OutOfMemoryError
)、类定义错误(NoClassDefFoundError
)等 。这些异常发生时,Java 虚拟机(JVM)一般会选择线程终止。
受检查异常 和 不受检查异常
受检查异常: Java 代码在编译过程中,如果受检查异常没有被 catch
/throw
处理的话,就没办法通过编译(IO异常, ClassNotFoundException, SQLException等)
不受检查异常: Java 代码在编译过程中 ,我们即使不处理不受检查异常也可以正常通过编译。(RuntimeException
及其子类都统称为非受检查异常, 例如 NullPointerException, NumberFormatException, ArrayIndexOutOfBoundsException, ClassCastException 等)
Throwable 类常用方法:
**public string getMessage()**
:返回异常发生时的简要描述**public string toString()**
:返回异常发生时的详细信息**public string getLocalizedMessage()**
:返回异常对象的本地化信息。使用Throwable
的子类覆盖这个方法,可以生成本地化信息。如果子类没有覆盖该方法,则该方法返回的信息与getMessage()
返回的结果相同**public void printStackTrace()**
:在控制台上打印Throwable
对象封装的异常信息在以下 3 种特殊情况下,
**finally**
块不会被执行:1. 在
try
或finally
块中用了System.exit(int)
退出程序。但是,如果System.exit(int)
在异常语句之后,finally
还是会被执行。2. 程序所在的线程死亡。
3. 关闭 CPU。
使用
try-with-resources
来代替try-catch-finally
1. 适用范围(资源的定义): 任何实现
java.lang.AutoCloseable
或者java.io.Closeable
的对象2. 关闭资源和 finally 块的执行顺序: 在
try-with-resources
语句中,任何 catch 或 finally 块在声明的资源关闭后运行举例说明: try-catch-finally 转 try-with-resources
```java //读取文本文件的内容 Scanner scanner = null; try { scanner = new Scanner(new File(“D://read.txt”)); while (scanner.hasNext()) {
} } catch (FileNotFoundException e) { e.printStackTrace(); } finally { if (scanner != null) {System.out.println(scanner.nextLine());
} }scanner.close();
// 使用 Java 7 之后的 try-with-resources
语句改造上面的代码:
try (Scanner scanner = new Scanner(new File(“test.txt”))) {
while (scanner.hasNext()) {
System.out.println(scanner.nextLine());
}
} catch (FileNotFoundException fnfe) {
fnfe.printStackTrace();
}
// 多资源的情况下, 使用分号分隔 try (BufferedInputStream bin = new BufferedInputStream(new FileInputStream(new File(“test.txt”))); BufferedOutputStream bout = new BufferedOutputStream(new FileOutputStream(new File(“out.txt”)))) { int b; while ((b = bin.read()) != -1) { bout.write(b); } } catch (IOException e) { e.printStackTrace(); }
<a name="YX5PP"></a>
## IO流 [IO流](https://www.yuque.com/zhangji-4wbp1/ibgfrx/ugbcsw)
<a name="lduf9"></a>
## 集合 [集合](https://www.yuque.com/zhangji-4wbp1/ibgfrx/wrm4w1)
<a name="eD5Q9"></a>
## BigDecimal
<a name="6XJPs"></a>
### 我们在使用BigDecimal时,为了防止精度丢失,推荐使用它的 **BigDecimal(String)** 构造方法来创建对象
<a name="NGub2"></a>
### 禁止使用 BigDecimal(double) 构造函数
![](https://cdn.nlark.com/yuque/0/2021/png/12544657/1626146632417-001c6891-22a7-4e56-8f5c-52c57e026c70.png#height=362&id=lvQNr&originHeight=724&originWidth=1676&originalType=binary&ratio=1&size=0&status=done&style=none&width=838)
<a name="WCQdR"></a>
## 枚举
```java
public class EnumDemo {
public static void main(String[] args) {
System.out.println(Status.PASS); // PASS
System.out.println(Status.PASS.fun()); // PASS_FUN
System.out.println(Status.PASS.getClass()); // class com.lius.javase.demo.EnumDemo$status
System.out.println(Status.PASS.name()); // PASS
System.out.println(Status.PASS.name().getClass()); // class java.lang.String
System.out.println("静态方法: values "+Arrays.toString(Status.values()));
System.out.println("静态方法: valueOf "+Status.valueOf("PASS"));
}
public enum Status {
PASS(1) {
@Override
public String fun() {
return "PASS_FUN";
}
},
NOT_PASS(2) {
@Override
public String fun() {
return "NOT_PASS_FUN";
}
};
/**
* 构造函数, 默认都是private修饰,而且只能是private. (private可以省略)
* 因为枚举类的实例不能让外界来创建!
*
* public Status() {} // 报错
*/
Status(int a) {
System.out.println(a + "hello!"); // 有几个枚举就会输出几遍
}
/**
* 抽象方法, 每个枚举必须实现此抽象方法
*/
public abstract String fun();
}
}
final,static,this,super 关键字
final
1. final 修饰的类不能被继承,final 类中的所有成员方法都会被隐式的指定为 final 方法;
2. final 修饰的方法不能被重写;
3. final 修饰的变量是常量,如果是基本数据类型的变量,则其数值一旦在初始化之后便不能更改;如果是引用类型的变量,则在对其初始化之后便不能让其指向另一个对象。
static
修饰成员变量和成员方法: 被 static 修饰的成员属于类,不属于单个这个类的某个对象,被类中所有对象共享,可以并且建议通过类名调用。被 static 声明的成员变量属于静态成员变量,静态变量 存放在 Java 内存区域的方法区。调用格式:类名.静态变量名
类名.静态方法名()
静态代码块: 静态代码块定义在类中方法外, 静态代码块在非静态代码块之前执行(静态代码块—>非静态代码块—>构造方法)。 该类不管创建多少对象,静态代码块只执行一次.
静态内部类(static 修饰类的话只能修饰内部类): 静态内部类与非静态内部类之间存在一个最大的区别: 非静态内部类在编译完成之后会隐含地保存着一个引用,该引用是指向创建它的外围类,但是静态内部类却没有。没有这个引用就意味着:1. 它的创建是不需要依赖外围类的创建。2. 它不能使用任何外围类的非 static 成员变量和方法。
静态导包(用来导入类中的静态资源,1.5 之后的新特性): 格式为:import static
这两个关键字连用可以指定导入某个类中的指定静态资源,并且不需要使用类名调用类中静态成员,可以直接使用类中静态成员变量和成员方法。
/**
* 静态成员, 静态代码块
*/
public class StaticDemo {
private int i1 ;
private static int i2;
static {
// i1 = 5; 静态代码块中无法使用非静态成员
i2 = 5;
System.out.println(i2);
i3 = 8;
// System.out.println(i3); 报错, 对于定义在静态代码块中的静态变量, 可以赋值, 但不能访问
}
private static int i3;
}
/**
* 使用静态内部类 实现单例模式
*
* 当 Singleton 类加载时,静态内部类 SingletonHolder 没有被加载进内存。
* 只有当调用 `getUniqueInstance()`方法从而触发 `SingletonHolder.INSTANCE` 时
* SingletonHolder 才会被加载,此时初始化 INSTANCE 实例,并且 JVM 能确保 INSTANCE 只被实例化一次。
*
* 这种方式不仅具有延迟初始化的好处,而且由 JVM 提供了对线程安全的支持。
*/
class Singleton {
// 私有化构造方法
private Singleton(){}
// 声明为 private 表明静态内部该类只能在该 Singleton 类中被访问
private static class SingletonHolder {
private static final Singleton instance = new Singleton();
}
public static Singleton getSingleton() {
return SingletonHolder.instance;
}
}
this 用于引用类的当前实例
class Manager {
Employees[] employees;
void manageEmployees() {
int totalEmp = this.employees.length;
System.out.println("Total employees: " + totalEmp);
this.report();
}
void report() { }
}
在上面的示例中,this 关键字用于两个地方:
- this.employees.length:访问类 Manager 的当前实例的变量。
- this.report():调用类 Manager 的当前实例的方法。
此关键字是可选的,这意味着如果上面的示例在不使用此关键字的情况下表现相同。 但是,使用此关键字可能会使代码更易读或易懂。
super 用于从子类访问父类的变量和方法
public class Super {
protected int number;
protected showNumber() {
System.out.println("number = " + number);
}
}
public class Sub extends Super {
void bar() {
super.number = 10;
super.showNumber();
}
}
Sub 类访问父类成员变量 number 并调用其父类 Super 的 `showNumber()` 方法。