数据类型
定义:Java语言是强类型语言,对于每一种数据都定义了明确的具体的数据类 型,在内存中分配了不同
大小的内存空间
基础类型
1.八个基础类型
类型 | 类型名称 | 关键字 | 占用内存 | 取值范围 | 作为成员变量的默认值 |
---|---|---|---|---|---|
整型 | 字节型 | byte | 1字节 | -128(-2^7)~ 127(2^7-1) | 0 |
整型 | 短整型 | short | 2字节 | -32768(-2^15)~ 32767(2^15-1) | 0 |
整型 | 整型 | int | 4字节 | -2147483648(-2^31)~ 2147483647(2^31-1) | 0 |
整型 | 长整型 | long | 8字节 | -89223372036854775808(-2^63)~ 9223372036854775807(2^63-1) | 0L |
浮点型 | 单精度浮点型 | float | 4字节 | -3403E38 ~ 3403E38 | 0.0F |
浮点型 | 双精度浮点型 | double | 8字节 | -1798E308~ 1798E308 | 0.0D |
字符型 | 字符型 | char | 2字节 | 表示一个字符,如('a','A','家') | '\u0000' |
布尔型 | 布尔型 | boolean | 1字节 | 只有两个值,true或false | false |
2.什么是隐式转换和显式转换
隐式类型转换是指将一个小范围的数据类型自动转换为一个大范围的数据类型,而显式类型转换则是指将一个大范围的数据类型强制转换为一个小范围的数据类型 。
例如,int类型可以自动转换为long类型,float类型可以自动转换为double类型。
3.switch能作用在哪些类型上
java5以前,switch(expr)中只能是byte,short,char,int。java5开始,expr引用了enum类型,java7开始,引用字符串(String)
引用类型
- 类(class)
- 接口(interface)
- 数组([])
缓存池
new Integer(123) 与 Integer.valueOf(123) 的区别在于:
- new Integer(123) 每次都会新建一个对象
- Integer.valueOf(123) 会使用缓存池中的对象,多次调用会取得同一个对象的引用。
valueOf()方法的实现,先判断值是否在缓存池中,在则直接返回缓存池的内容.
缓冲池范围内的基本类型,在自动装箱过程中,编译器会先检查缓冲池中是否已经存在相同值的对象,如果存在则直接返回该对象的引用,否则就创建一个新的对象并返回其引用。
String
底层数据结构
JDK9之前:final 修饰的char[]数组
JDK9之后:final 修饰的byte[]数组
从char[]改为byte[]的好处:
1)节省内存,char占用两个字节,byte只需要一个字节
2)减少GC的次数:减少了内存使用之后,必然垃圾回收次数也会相对应减少
final修饰作用是不可变,不可变的好处
1)可以缓存hash值,hash不可变,只需要进行一次计算
2)String pool的需要.如果一个String对象已经被创建过了,那么就会从String Pool中取得引用.只有 String 是不可变的,才可能使用 String Pool。
3)安全性,经常作为参数,不可变性可以保证参数不可变.
4)线程安全,String 不可变性天生具备线程安全,可以在多个线程中安全地使用。
String,StringBuffer and StringBuilder区别
1)可变性:
String不可变
StringBuffer和StringBuilder可变
2)线程安全
String不可变,因此线程安全
StringBuilder非线程安全
StringBuffer线程安全,因内部使用synchronized进行同步
String.intern()
使用 String.intern() 可以保证相同内容的字符串变量引用同一的内存对象。
HotSpot中字符串常量池保存哪里?永久代?方法区还是堆区?
HotSpot是JVM的一种实现。HotSpot的编译器主要有两个:C1和C2。C1被称作Client Compiler(客户端编译器),它的编译速度快,但是产生的代码质量相对较低。C2被称作Server Compiler(服务器端编译器),它的编译速度慢,但是产生的代码质量高,能够进行更多的优化。HotSpot根据方法的执行次数来判断是否需要使用C2编译器进行即时编译。如果方法被反复调用,就会被JIT编译器(Just-In-Time Compiler)编译成本地机器指令,这样就可以获得比解释执行更好的性能。
HotSpot的垃圾回收器非常强大,它使用了分代垃圾回收算法。HotSpot将堆分为新生代和老年代。新生代一般只存放短期使用的对象,它又被分成Eden区、Survivor0区和Survivor1区。当内存不足时,新生代中的可达对象会被复制到Survivor区,而不可达对象会被直接回收。老年代则是存放长期存活的对象以及大对象。当老年代内存不足时,就会触发Full GC(整堆垃圾回收),这个过程开销比较大,应该尽量避免。在HotSpot中,可以通过调整各种参数来优化垃圾回收器的性能。
1)运行时常量池(Runtime Constant Pool)是虚拟机规范中是方法区的一部分,在加载类和结构到虚拟机后,就会创建对应的运行时常量池;而字符串常量池是这个过程中常量字符串的存放位置。所以从这个角度,字符串常量池属于虚拟机规范中的方法区,它是一个逻辑上的概念;而堆区,永久代以及元空间是实际的存放位置。
编码
Java语言采用Unicode编码标准,Unicode(标准码),它为每个字符制订了一 个唯一的数值,因此在
任何的语言,平台,程序都可以放心的使用。
注释
定义:用于解释说明程序的文字
分类:
- 单行注释 格式://注释文字
- 多行注释 格式:/*注释文字*/
- 文档注释 格式:/**注释文字*/
访问修饰符
定义:使用访问修饰符来保护类,变量,方法和构造方式的访问(个人理解:就是对修饰的变量或者方法的访问权限地控制)
修饰符 | 当前类 | 同包 | 子类 | 其他包 |
---|---|---|---|---|
private | √ | X | X | X |
default | √ | √ | X | X |
protected | √ | √ | √ | X |
public | √ | √ | √ | √ |
抽象类与接口
1.抽象类
抽象类和抽象方法都用abstract关键字生命,类一般会包含方法,方法一定位于类中。
抽象类和普通类最大的区别是,抽象类不能被实例化,需要继承抽象类才能实例化其子类。
2.接口
接口是抽象类的延伸,在 Java 8 之前,它可以看成是一个完全抽象的类,也就是说它不能有任何的方法实现。Java 8 开始,接口也可以拥有默认的方法实现,这是因为不支持默认方法的接口的维护成本太高了。在 Java 8 之前,如果一个接口想要添加新的方法,那么要修改所有实现了该接口的类。
接口的成员(字段 + 方法)默认都是 public 的,并且不允许定义为 private 或者 protected。
接口的字段默认都是 static 和 final 的。
3.抽象类和接口的比较
3.1)抽象类提供了IS-A的关系,子类必须替换所有父类的对象;接口更像LIKE-A关系,提供一种方法实现契约
3.2)使用上,一个类可以实现多个接口,但只能继承一个抽象类
3.3)接口的字段只能是 static 和 final 类型的,而抽象类的字段没有这种限制。
3.4)接口的成员只能是 public 的,而抽象类的成员可以有多种访问权限
4.使用选择
4.1)使用接口:
- 需要让不相关的类都实现一个方法,例如不相关的类都可以实现 Compareable 接口中的 compareTo() 方法;
- 需要使用多重继承。
4.2)使用抽象类:
- 需要在几个相关的类中共享代码。需要能控制继承来的成员的访问权限,而不是都为 public。
- 需要继承非静态和非常量字段。
在很多情况下,接口优先于抽象类,因为接口没有抽象类严格的类层次结构要求,可以灵活地为一个类添加行为。并且从 Java 8 开始,接口也可以有默认的方法实现,使得修改接口的成本也变的很低。
重载与重写
1.重写(Override)
存在于继承体系中,指子类实现了一个与父类在方法声明上完全相同的一个方法。
为了满足里式替换原则,重写有以下两个限制:
- 子类方法的访问权限必须大于等于父类方法;
- 子类方法的返回类型必须是父类方法返回类型或为其子类型。
使用 @Override 注解,可以让编译器帮忙检查是否满足上面的两个限制条件。
2.重载(Overload)
存在于同一个类中,指一个方法与已经存在的方法名称上相同,但是参数类型、个数、顺序至少有一个不同。
应该注意的是,返回值不同,其它都相同不算是重载。
运算符
&和&&的区别
&运算符有两个用法:(1)按位与;(2)逻辑与。
&&运算符是短路与运算。要求运算符左右两边的布尔值都是true。&&之所以称
为短路运算,是因为如果&&左边的表达式的值是 false,右边的表达式会被直接短路掉,不会进行运
算。
注意:逻辑或运算符(|)和短路或运算符(||)的差别也是如此
关键字
final
用于修饰类、属性和方法:
- 被final修饰的类不可以被继承
- 被final修饰的方法不可以被重写
- 被final修饰的变量不可以被改变,被final修饰不可变的是变量的引用,而不是引用指向的内容,引用指向的内容是可以改变的
final finally finalize区别
- final可以修饰类、变量、方法,修饰类表示该类不能被继承、修饰方法表示该方法不能被重写、修
饰变量表示该变量是一个常量不能被重新赋值。 - finally一般作用在try-catch代码块中,在处理异常的时候,通常我们将一定要执行的代码方法
finally代码块中,表示不管是否出现异常,该代码块都会执行,一般用来存放一些关闭资源的代
码。 - finalize是一个方法,属于Object类的一个方法,而Object类是所有类的父类,该方法一般由垃圾
回收器来调用,当我们调用System.gc() 方法的时候,由垃圾回收器调用finalize(),回收垃圾,一个对象是否可回收的最后判断。
this关键字的用法
this代表对象本身
super关键字的用法
super指自己超(父)类对象的一个指针,而这个超类指的是离自己最近的一个父类
super(参数):调用父类中的某一个构造函数(应该为构造函数中的第一条语句)。
this(参数):调用本类中另一种形式的构造函数(应该为构造函数中的第一条语句)。
static关键字
作用:创建独立于具体对象的域变量或者方法,用于形成静态代码块以优化程序性能。在类初次被加载的时候,会按照static块的顺序来执行每个static块,且只会执行一次。优化程序性能,是因为只会在类加载的时候执行一次。
- 被static修饰的变量或者方法不属于任何实例对象,而是属于类。
- 被static修饰的变量和对象,方法存储在方法区的静态常量池中;是共享的;
- 可以在没有创建任何对象的前提下,仅仅通过类本身来调用static方法
- static修饰的代码块在main方法前执行,目的是修饰main方法
- static关键字修饰的方法或者变量不需要依赖于对象来进行访问,只要类被加载了,就可以通过类名去进行访问
- static方法是没有this的方法,在static方法内部不能调用非静态方法,反过来是可以的
- static变量值在类加载的时候分配空间,以后创建类对象的时候不会重新分配。赋值的话,是可以任意赋值的
初始化顺序
静态变量和静态语句块优先于实例变量和普通语句块,静态变量和静态语句块的初始化顺序取决于它们在代码中的顺序。最后才是构造函数的初始化。
存在继承的情况下,初始化顺序为:
- 父类(静态变量、静态语句块)
- 子类(静态变量、静态语句块)
- 父类(实例变量、普通语句块)
- 父类(构造函数)
- 子类(实例变量、普通语句块)
- 子类(构造函数)
流程控制语句
break ,continue ,return 的区别及作用
break 跳出总上一层循环,不再执行循环(结束当前的循环体)continue 跳出本次循环,继续执行下次循
环(结束正在执行的循环 进入下一个循环条件)
return 程序返回,不再执行下面的代码(结束当前的方法 直接返回)
Object通用方法
//返回对象的运行时类。它用于获取对象的类型信息,可用于各种目的,例如检查两个对象是否属于同一类或使用反射创建特定类的实例。
public final native Class<?> getClass()
//用于返回对象的哈希码。哈希码是一个整数,它是由对象的内部状态计算出来的,通常用于在哈希表中快速查找对象。hashCode()方法的默认实现是根据对象的内存地址计算哈希码。但是,许多类都覆盖了这个方法,以便根据对象的状态生成更有意义的哈希码。如果两个对象相等(即equals()方法返回true),则它们的哈希码必须相等。
public native int hashCode()
//用于比较两个对象是否相等。在Java中,所有的类都继承自Object类,因此所有对象都可以使用equals()方法进行比较。默认情况下,equals()方法的实现是比较对象的内存地址。但是,许多类都覆盖了这个方法,以便根据对象的状态生成更有意义的比较结果。
public boolean equals(Object obj)
//用于创建一个对象的副本。它返回一个新的对象,该对象具有与原始对象相同的属性和状态。默认情况下,clone()方法的实现是浅拷贝,即只复制对象本身及其引用类型的成员变量的值,而不复制引用类型的成员变量所指向的对象。如果需要进行深拷贝,即复制引用类型的成员变量所指向的对象,那么需要在类中重写clone()方法,并在其中手动复制引用类型的成员变量。
protected native Object clone() throws CloneNotSupportedException
//用于将对象转换为字符串表示形式。该方法返回一个字符串,其中包含有关对象的信息,例如对象的类名、字段值等。默认情况下,toString()方法的实现是基于对象的类名和哈希码(即对象的内存地址)来生成字符串表示形式的。但是,许多类都覆盖了这个方法,以便根据对象的状态生成更有意义的字符串表示形式。
public String toString()
//用于唤醒在当前对象上等待的单个线程。该方法通常与Object类的synchronized关键字一起使用,以确保多个线程可以安全地访问共享资源。当一个线程调用了某个对象的synchronized方法或synchronized代码块时,它会获取该对象的监视器锁(monitor lock),并进入等待状态。如果其他线程也想访问该对象的同步代码块,它们必须首先释放该对象的监视器锁,然后才能进入等待状态。notify()方法用于唤醒在当前对象上等待的单个线程。如果有多个线程在等待,notify()方法只会唤醒其中一个线程。如果没有线程在等待,notify()方法将不会有任何效果。
public final native void notify()
//用于唤醒在当前对象上等待的所有线程。该方法通常与Object类的synchronized关键字一起使用,以确保多个线程可以安全地访问共享资源。当一个线程调用了某个对象的synchronized方法或synchronized代码块时,它会获取该对象的监视器锁(monitor lock),并进入等待状态。如果其他线程也想访问该对象的同步代码块,它们必须首先释放该对象的监视器锁,然后才能进入等待状态。notifyAll()方法用于唤醒在当前对象上等待的所有线程。如果有多个线程在等待,notifyAll()方法将唤醒所有线程。如果没有线程在等待,notifyAll()方法将不会有任何效果。
public final native void notifyAll()
public final native void wait(long timeout) throws InterruptedException
public final void wait(long timeout, int nanos) throws InterruptedException
//用于让当前线程等待,直到其他线程调用相同对象的notify()或notifyAll()方法唤醒它。该方法通常与Object类的synchronized关键字一起使用,以确保多个线程可以安全地访问共享资源。当一个线程调用了某个对象的synchronized方法或synchronized代码块时,它会获取该对象的监视器锁(monitor lock),并进入等待状态。如果其他线程也想访问该对象的同步代码块,它们必须首先释放该对象的监视器锁,然后才能进入等待状态。wait()方法用于让当前线程等待,直到其他线程调用相同对象的notify()或notifyAll()方法唤醒它。如果没有其他线程调用notify()或notifyAll()方法,当前线程将一直等待,直到其他线程调用notify()或notifyAll()方法或者当前线程被中断。
public final void wait() throws InterruptedException
//用于在垃圾回收器准备回收对象之前执行清理操作。该方法通常用于释放资源、关闭文件句柄等操作。当一个对象不再被引用时,垃圾回收器将开始回收该对象的内存空间。在回收之前,垃圾回收器会调用该对象的finalize()方法来执行清理操作。如果该对象的finalize()方法没有显式地被重写,那么它将执行默认的清理操作,例如关闭文件句柄、释放网络连接等。需要注意的是,finalize()方法并不是线程安全的,因此在多线程环境下使用时需要特别小心。此外,由于垃圾回收器的调度是不确定的,因此不能依赖finalize()方法来执行重要的清理操作。
protected void finalize() throws Throwable {}
equals() 与 ==
- 对于基本类型,== 判断两个值是否相等,基本类型没有 equals() 方法。
- 对于引用类型,== 判断两个变量是否引用同一个对象,而 equals() 判断引用的对象是否等价。
浅拷贝
拷贝对象和原始对象的引用类型引用同一个对象。
深拷贝
拷贝对象和原始对象的引用类型引用不同对象。
clone() 的替代方案使用
clone() 方法来拷贝一个对象即复杂又有风险,它会抛出异常,并且还需要类型转换。Effective Java 书上讲到,最好不要去使用 clone(),可以使用拷贝构造函数或者拷贝工厂来拷贝一个对象。
反射
每个类都有一个 Class 对象,包含了与类有关的信息。当编译一个新类时,会产生一个同名的 .class 文件,该文件内容保存着 Class 对象。
类加载相当于 Class 对象的加载。类在第一次使用时才动态加载到 JVM 中,可以使用Class.forName("com.mysql.jdbc.Driver") 这种方式来控制类的加载,该方法会返回一个 Class 对象。
反射可以提供运行时的类信息,并且这个类可以在运行时才加载进来,甚至在编译时期该类的 .class 不存在也可以加载进来。
Class 和 java.lang.reflect 一起对反射提供了支持,java.lang.reflect 类库主要包含了以下三个类:
- Field : 可以使用 get() 和 set() 方法读取和修改 Field 对象关联的字段;
- Method : 可以使用 invoke() 方法调用与 Method 对象关联的方法;
- Constructor : 可以用 Constructor 创建新的对象。
异常
Throwable 可以用来表示任何可以作为异常抛出的类,分为两种: Error 和 Exception。
其中 Error 用来表示 JVM 无法处理的错误,
Exception 分为两种:
- 受检异常 : 需要用 try...catch... 语句捕获并进行处理,并且可以从异常中恢复;
- 非受检异常 : 是程序运行时错误,例如除 0 会引发 Arithmetic Exception,此时程序崩溃并且无法恢复。