# 基本数据类型和引用类型的区别

Java中一共有四类八种基本数据类型

分类 数据类型
整型 byteshortintlong
浮点类型 floatdouble
字符型 char
逻辑型 boolean

除掉这4类8种基本类型,其它的都是对象,也就是引用类型,包括数组。

  • 存储位置
    • 基本数据类型在被创建时,在栈上给其划分一块内存,将数值直接存储在栈(Stack)上
    • 引用数据类型在被创建时,首先要在栈上给其引用分配一块内存,而对象的具体信息都存储在堆内存上
  • 传递方式
    • 基本类型是按值传递的。
    • 引用类型是按照引用传递的。

# 访问控制修饰符

  • default (即默认,什么也不写): 在同一包内可见,不使用任何修饰符。使用对象:类、接口、变量、方法。
  • private : 在同一类内可见。使用对象:变量、方法。 注意:不能修饰类(外部类)
  • public : 对所有类可见。使用对象:类、接口、变量、方法
  • protected : 对同一包内的类和所有子类可见。使用对象:变量、方法。 注意:不能修饰类(外部类)

# == 和 equals(重要)

==用于判断两个对象的地址是不是相等,即判断两个对象是不是同一个对象,(基本数据类型,比较的是值,引用类型则比较的是地址。)

equals它的作用也是用于判断两个对象是不是相等,分为两种情况

  • 第一种情况,类没有覆盖equals()方法,则通过equals()比较该类的两个对象时,等价于通过==比较两个对象。

  • 第二种情况,类覆盖equals()方法,则根据覆盖equals()方法来比较两个对象的内容是否相等,如果两个对象的内容相同,则返回true。

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 的equals()方法是被重写过的,因为object的equals()方法是比较对象的内存地址,而String的equals()方法比较的是对象的值。 当创建String类型的对象的时候,虚拟机会在常量池查询有没有已经存在的值和要创建的值相同的对象,如果有就把它赋值给当前的引用,如果没有就在常量池重新创建一个String对象。

# 接口和抽象类的区别

  1. 接口内的所有方法都不能有具体实现,而抽象类可以有非抽象的方法。
  2. 接口中除了static、final变量,不能有其他变量,而抽象类则不一定。
  3. 一个类可以实现多个接口,但只能有一个抽象类,接口本身可以通过extends关键字扩展多个接口。
  4. 接口方法默认修饰符是public,抽象方法可以用public、protected和default这些修饰符。
  5. 从设计层面上来说,抽象是对类的抽象,是一种模板设计,而接口是对行为的抽象,是一种行为的规范。

# hashCode 和equals (重要)

hashCode()介绍

hashcode方法的作用就是获取哈希码,也就是散列码,它实际上返回的就是一个int整数,这个哈希码的作用就是确定该对象在哈希表种的位置。hashCode()方法定义在JDK的Object.java种,因此每一个类都包含有hashCode()函数。

哈希表存储的是键值对

hashCode()equals()方法相关规定:

  • 如果两个对象相等,则它们的hashCode一定相等。
  • 两个对象相等,对两个对象分别调用equals()方法都返回true。
  • 两个对象拥有相同的hashCode,它们也不一定相等。因为在散列表中,hashCode()相等,即两个键值对的哈希值相等。然而哈希值相等,并不一定能得出键值对相等。补充说一句:“两个不同的键值对,哈希值相等”,这就是哈希冲突。
  • 因此,equals()方法被覆盖过,则它们的hashCode方法也必须覆盖。
  • hashCode()的默认行为是对堆上的对象产生独特值,如果没有重写hashCode()方法,则该类的两个对象无论如何都不会相等。

# String、StringBuilder、StringBuffer的区别是什么?

  • 可变性:简单的说,String类中使用final关键字修饰字符数组来保存字符串,所以String是不可变的。而StringBuilder和StringBuffer都继承自AbstractStringBuilder类,在AbstractStringBuilder中,也是使用字符数组来保存字符串,但是没有使用final关键字,所以是可变的。
  • 线程安全性:String中的对象都是不可变的,可以理解为常量,所以是线程安全的。AbstractStringBuilder是StringBuilder和StringBuffer的公共父类,定义了字符串的一些基本操作。StringBuffer对方法增加了同步锁或者对调用的方法增加了同步锁,所以是线程安全的。StringBuilder没有对方法增加同步锁,所以是线程不安全的。
  • 性能:每次对String类型的变量进行改变的时候,都会生成一个新的String对象,然后将指针指向新的String对象。StringBuffer每次都对StringBuffer对象本身进行操作,而不是生成新的对象并改变对象引用。相同情况下,使用StringBuilder比使用StringBuffer仅能够获得10%-15%的性能提升,但却需要冒更多的线程不安全的风险。

总结来说,操作少量的数据使用String,单线程操作字符缓冲区下大量字符串使用StringBuilder,多线程操作字符缓冲区下大量字符串使用StringBuffer。

# String常用方法

# 用字符数组value创建一个String对象

方法 含义
public String(char[] value) 用字符数组value创建一个String对象
public String(char chars[], int x, int n) 用字符数组以x开始的n个字符创建一个String对象

# 获取字符串某一位置的字符

方法 含义
public char charAt(int index)

# 获取字符串的子串

方法 含义
public String substring(int beginIndex) 该方法从beginIndex位置起,* //从当前字符串中取出剩余的字符作为一个新的字符串返回。
public String substring(int beginIndex, int endIndex) 该方法从beginIndex位置起,从当前字符串中取出到endIndex-1位置的字符作为一个新的字符串返回。

# 字符串的比较

方法 含义
public int compareTo(String str) 该方法是对字符串内容按字典顺序进行大小比较,
通过返回的整数值指明当前字符串与参数字符串的大小关系。
若当前对象比参数大则返回正整数,反之返回负整数,相等返回0。
public int compareToIgnoreCase (String str) 与compareTo方法相似,但忽略大小写。
public boolean equals(Object obj) 比较当前字符串和参数字符串,在两个字符串相等的时候返回true,否则返回false。
public boolean equalsIgnoreCase(String str) 与equals方法相似,但忽略大小写。

# 查找子串在字符串中的位置

方法 含义
public int indexOf(String str) 用于查找当前字符串中字符或子串,返回字符或子串
在当前字符串中从左边起首次出现的位置,若没有出现则返回-1。
public int indexOf(String str, intfromIndex) 该方法与第一种类似,区别在于该方法从fromIndex位置向后查找。
public int lastIndexOf(String str) 该方法与第一种类似,区别在于该方法从字符串的末尾位置向前查找。
public int lastIndexOf(String str, intfromIndex) 该方法与第二种方法类似,区别于该方法从fromIndex位置向前查找。

# 字符串中字符的大小写转换

方法 含义
public String toLowerCase() 返回将当前字符串中所有字符转换成小写后的新串
public String toUpperCase() 返回将当前字符串中所有字符转换成大写后的新串

# 字符串两端去空格

方法 含义
String trim() 去除字符串两端的空格,中间的空格不变,一般用于登陆注册时

# 将字符串分割成字符串数组

方法 含义
String[] split(String str) 将字符串分割成字符串数组

# 替换字符串

方法 含义
public String replace(char oldChar, charnewChar) 用字符newChar替换当前字符串中所有的oldChar字符,并返回一个新的字符串。
public String replaceFirst(String regex,String replacement) 该方法用字符replacement的内容替换当前字符串中遇到的第一个和字符串regex相匹配的子串,并将新的字符串返回。
public String replaceAll(String regex,String replacement) 该方法用字符replacement的内容替换当前字符串中遇到的所有和字符串regex相匹配的子串,应将新的字符串返回。

# 基本类型转换为字符串

方法 含义
static String valueOf(xxx xx) 基本类型转换为字符串

# 字符常量和字符串常量的区别

  1. 形式上 :字符常量是一个单引号引起的一个字符,字符串常量是使用双引号引起来的若干个字符。
  2. 含义上 :字符常量相当于一个整型值可以参加表达式计算,字符串常量代表一个地址值。
  3. 内存占用上 :字符常量只占两个子节,字符串常量占若干个子节。

# 重载和重写的区别

  • 重载 :发生在同一个类中,方法名必须相同,参数类型不同、个数不同、顺序不同,方法返回值和访问修饰符可以不同。
  • 重写 :重写是子类对父类可以访问的方法的实现过程进行重新编写,发生在子类中,方法名、参数列表必须相同,返回值范围小于等于父类,抛出的异常小于等于父类,访问修饰符范围大于等于父类。但是,如果父类方法访问修饰符是private,则不能重写该方法。也就是方法提供的行为改变,而方法的外貌并没有改变。

# Java序列化和反序列化

# 序列化和反序列化的含义

  • 序列化:把对象转换为字节序列的过程称为对象的序列化.
  • 反序列化:把字节序列恢复为对象的过程称为对象的反序列化.

# 对象序列化的主要作用

  • 把对象的字节序列永久地保存到硬盘上,通常存放在一个文件中;
  • 在网络上传送对象的字节序列。

# 实现Serializable 接口的意义

在 Java 中实现了 Serializable 接口后, JVM 会在底层帮我们实现序列化和反序列化,如果我们不实现 Serializable 接口, 那自己去写一套序列化和反序列化代码。

# 显式指定serialVersionUID的含义

  • 如果不显示指定 serialVersionUID, JVM 在序列化时会根据属性自动生成一个 serialVersionUID, 然后与属性一起序列化,再进行持久化或网络传输。在反序列化时,JVM 会再根据属性自动生成一个新版 serialVersionUID,然后将这个新版 serialVersionUID 与序列化时生成的旧版 serialVersionUID 进行比较,如果相同则反序列化成功, 否则报错。

  • 如果显示指定了 serialVersionUID, JVM 在序列化和反序列化时仍然都会生成一个 serialVersionUID, 但值为我们显示指定的值,这样在反序列化时新旧版本的 serialVersionUID 就一致了.在实际开发中, 不显示指定 serialVersionUID 的情况会导致什么问题?如果我们的类写完后不再修改,那当然不会有问题。但这在实际开发中是不可能的,我们的类会不断迭代,一旦类被修改了,那旧对象反序列化就会报错。所以在实际开发中, 我们都会显示指定一个 serialVersionUID,值是多少无所谓, 只要不变就行。

# 指定某些属性不序列化

可以使用 transient关键字修饰属性,这样该属性就不会被序列化, static属性也不会被序列化。

# static 属性不会被序列化的原因

因为序列化是针对对象而言的,而 static 属性优先于对象存在, 随着类的加载而加载, 所以不会被序列化.看到这个结论, 是不是有人会问, serialVersionUID 也被 static 修饰, 为什么 serialVersionUID 会被序列化? 其实 serialVersionUID 属性并没有被序列化, JVM 在序列化对象时会自动生成一个 serialVersionUID, 然后将我们显示指定的 serialVersionUID 属性值赋给自动生成的 serialVersionUID。

# final关键字的一些总结

final关键字主要作用在三个地方:变量、方法、类。

  1. 对于一个final变量,如果是基本数据类型的变量,则其数值在初始化后不能改变,如果是引用类型的变量,则在其初始化之后不能指向另一个对象。
  2. 当用final修饰一个类时,表示这个类不能被继承。final类中的所有成员方法都会被隐式地指定为final方法。
  3. 使用final方法的原因一共有两个:一个是防止任何继承类修改它的含义;另外一个是效率,在早期的java实现版本中,会将final方法转化为内嵌调用,但是如果方法过于庞大,则无法通过内嵌调用获得提升,类中所有的private方法都隐式地指定为final方法。

# 浅拷贝和深拷贝

浅拷贝:对于基本数据类型进行值传递,对于引用数据类型进行引用传递般的拷贝。

深拷贝:对基本数据类型进行值传递,对于引用数据类型,创建一个新的对象,并复制其内容。

LastUpdated: 3/20/2021, 11:37:24 AM