面霸篇:高频 Java 基础问题(核心卷一)(四)

简介: 接上文。

String、StringBuilder、StringBuffer 有什么区别?


可变性


String类中使用字符数组保存字符串,private final char value[],所以string对象是不可变的。StringBuilder与StringBuffer都继承自AbstractStringBuilder类,在AbstractStringBuilder中也是使用字符数组保存字符串,char[] value,这两种对象都是可变的。


线程安全性


String中的对象是不可变的,也就可以理解为常量,线程安全。AbstractStringBuilder是StringBuilder与StringBuffer的公共父类,定义了一些字符串的基本操作,如expandCapacity、append、insert、indexOf等公共方法。


StringBuffer对方法加了同步锁或者对调用的方法加了同步锁,所以是线程安全的。


StringBuilder并没有对方法进行加同步锁,所以是非线程安全的。


性能


每次对String 类型进行改变的时候,都会生成一个新的String对象,然后将指针指向新的String 对象。


StringBuffer每次都会对StringBuffer对象本身进行操作,而不是生成新的对象并改变对象引用。相同情况下使用StirngBuilder 相比使用StringBuffer 仅能获得10%~15% 左右的性能提升,但却要冒多线程不安全的风险。


对于三者使用的总结


如果要操作少量的数据用 = String


单线程操作字符串缓冲区 下操作大量数据 = StringBuilder


多线程操作字符串缓冲区 下操作大量数据 = StringBuffer


String


String 是 Java 语言非常基础和重要的类,提供了构造和管理字符串的各种基本逻辑。它是典型的 Immutable 类,被声明成为 final class,所有属性也都是 final 的。


也由于它的不可变性,类似拼接、裁剪字符串等动作,都会产生新的 String 对象。


StringBuilder


StringBuilder 是 Java 1.5 中新增的,在能力上和 StringBuffer 没有本质区别,但是它去掉了线程安全的部分,有效减小了开销,是绝大部分情况下进行字符串拼接的首选。


StringBuffer


StringBuffer 是为解决上面提到拼接产生太多中间对象的问题而提供的一个类,我们可以用 append 或者 add 方法,把字符串添加到已有序列的末尾或者指定位置。StringBuffer 本质是一个线程安全的可修改字符序列,它保证了线程安全,也随之带来了额外的性能开销,所以除非有线程安全的需要,不然还是推荐使用它的后继者,也就是 StringBuilder。


HashMap 使用 String 作为 key有什么好处


HashMap 内部实现是通过 key 的 hashcode 来确定 value 的存储位置,因为字符串是不可变的,所以当创建字符串时,它的 hashcode 被缓存下来,不需要再次计算,所以相比于其他对象更快。


接口和抽象类有什么区别?


抽象类是用来捕捉子类的通用特性的。接口是抽象方法的集合。


接口和抽象类各有优缺点,在接口和抽象类的选择上,必须遵守这样一个原则:


  • 行为模型应该总是通过接口而不是抽象类定义,所以通常是优先选用接口,尽量少用抽象类。


  • 选择抽象类的时候通常是如下情况:需要定义子类的行为,又要为子类提供通用的功能。


相同点


  • 接口和抽象类都不能实例化


  • 都位于继承的顶端,用于被其他实现或继承


  • 都包含抽象方法,其子类都必须覆写这些抽象方法


接口


接口定义了协议,是面向对象编程(封装、继承多态)基础,通过接口我们能很好的实现单一职责、接口隔离、内聚。


  • 不能实例化;


  • 不能包含任何非常量成员,任何 field 都是隐含着 public static final 的意义;


  • 同时,没有非静态方法实现,也就是说要么是抽象方法,要么是静态方法。


Java8 中接口中引入默认方法和静态方法,并且不用强制子类来实现它。以此来减少抽象类和接口之间的差异。


抽象类


抽象类是不能实例化的类,用 abstract 关键字修饰 class,其目的主要是代码重用。


从设计层面来说,抽象类是对类的抽象,是一种模板设计,接口是行为的抽象,是一种行为的规范。


除了不能实例化,形式上和一般的 Java 类并没有太大区别。


可以有一个或者多个抽象方法,也可以没有抽象方法。抽象类大多用于抽取相关 Java 类的共用方法实现或者是共同成员变量,然后通过继承的方式达到代码复用的目的。


码老湿,抽象类能用 final 修饰么?


不能,定义抽象类就是让其他类继承的,如果定义为 final 该类就不能被继承,这样彼此就会产生矛盾,所以 final 不能修饰抽象类


值传递


当一个对象被当作参数传递到一个方法后,此方法可改变这个对象的属性,并可返回变化后的结果,那么这里到底是值传递还是引用传递?


是值传递。


Java 语言的方法调用只支持参数的值传递。当一个对象实例作为一个参数被传递到方法中时,参数的值就是对该对象的引用。


对象的属性可以在被调用过程中被改变,但对对象引用的改变是不会影响到调用者的。


为什么 Java 只有值传递?


首先回顾一下在程序设计语言中有关将参数传递给方法(或函数)的一些专业术语。按值调用(call by value)表示方法接收的是调用者提供的值,而按引用调用(call by reference)表示方法接收的是调用者提供的变量地址。


一个方法可以修改传递引用所对应的变量值,而不能修改传递值调用所对应的变量值。

它用来描述各种程序设计语言(不只是Java)中方法参数传递方式。


Java程序设计语言总是采用按值调用。也就是说,方法得到的是所有参数值的一个拷贝,也就是说,方法不能修改传递给它的任何参数变量的内容。


基本数据类型


例子如下:


public static void main(String[] args) {
    int num1 = 10;
    int num2 = 20;
    swap(num1, num2);
    System.out.println("num1 = " + num1);
    System.out.println("num2 = " + num2);
}
public static void swap(int a, int b) {
    int temp = a;
    a = b;
    b = temp;
    System.out.println("a = " + a);
    System.out.println("b = " + b);
}


执行结果:


a = 20
b = 10
num1 = 10
num2 = 20


解析:


image.png


在swap方法中,a、b的值进行交换,并不会影响到 num1、num2。


因为,a、b中的值,只是从 num1、num2 的复制过来的。


也就是说,a、b相当于num1、num2 的副本,副本的内容无论怎么修改,都不会影响到原件本身。


对象引用类型


public static void main(String[] args) {
        int[] arr = { 1, 2, 3, 4, 5 };
        System.out.println(arr[0]);
        change(arr);
        System.out.println(arr[0]);
    }
    public static void change(int[] array) {
        // 将数组的第一个元素变为0
        array[0] = 0;
    }


结果:


1
0


解析:


image.png


array 被初始化 arr 的拷贝也就是一个对象的引用,也就是说 array 和 arr 指向的时同一个数组对象。 因此,外部对引用对象的改变会反映到所对应的对象上。


通过 example2 我们已经看到,实现一个改变对象参数状态的方法并不是一件难事。理由很简单,方法得到的是对象引用的拷贝,对象引用及其他的拷贝同时引用同一个对象。

很多程序设计语言(特别是,C++和Pascal)提供了两种参数传递的方式:值调用和引用调用。


有些程序员认为Java程序设计语言对对象采用的是引用调用,实际上,这种理解是不对的。


值传递和引用传递有什么区别?


值传递:指的是在方法调用时,传递的参数是按值的拷贝传递,传递的是值的拷贝,也就是说传递后就互不相关了。


引用传递:指的是在方法调用时,传递的参数是按引用进行传递,其实传递的引用的地址,也就是变量所对应的内存空间的地址。传递的是值的引用,也就是说传递前和传递后都指向同一个引用(也就是同一个内存空间)。


相关文章
|
6月前
|
存储 缓存 Java
最新Java基础系列课程--Day10-IO流文件处理
最新Java基础系列课程--Day10-IO流文件处理
|
6月前
|
存储 Java
最新Java基础系列课程--Day10-IO流文件处理(一)
最新Java基础系列课程--Day10-IO流文件处理
|
1月前
|
存储 缓存 Java
java基础:IO流 理论与代码示例(详解、idea设置统一utf-8编码问题)
这篇文章详细介绍了Java中的IO流,包括字符与字节的概念、编码格式、File类的使用、IO流的分类和原理,以及通过代码示例展示了各种流的应用,如节点流、处理流、缓存流、转换流、对象流和随机访问文件流。同时,还探讨了IDEA中设置项目编码格式的方法,以及如何处理序列化和反序列化问题。
67 1
java基础:IO流 理论与代码示例(详解、idea设置统一utf-8编码问题)
|
5月前
|
Java
【Java基础】输入输出流(IO流)
Java基础、输入输出流、IO流、流的概念、输入输出流的类层次结构图、使用 InputStream 和 OutputStream流类、使用 Reader 和 Writer 流类
168 2
|
2月前
|
安全 Java API
【Java面试题汇总】Java基础篇——String+集合+泛型+IO+异常+反射(2023版)
String常量池、String、StringBuffer、Stringbuilder有什么区别、List与Set的区别、ArrayList和LinkedList的区别、HashMap底层原理、ConcurrentHashMap、HashMap和Hashtable的区别、泛型擦除、ABA问题、IO多路复用、BIO、NIO、O、异常处理机制、反射
【Java面试题汇总】Java基础篇——String+集合+泛型+IO+异常+反射(2023版)
|
5月前
|
安全 Java
|
5月前
|
搜索推荐 算法 Java
【Java基础】 几种简单的算法排序
几种简单的JAVA算法排序
53 4
|
5月前
|
存储 缓存 Java
Java基础17-读懂Java IO流和常见面试题(二)
Java基础17-读懂Java IO流和常见面试题(二)
41 0
|
5月前
|
存储 Java Unix
Java基础17-读懂Java IO流和常见面试题(一)
Java基础16-读懂Java IO流和常见面试题(一)
74 0
|
6月前
|
Java
Java基础教程(12)-Java中的IO流
【4月更文挑战第12天】Java IO涉及输入输出,包括从外部读取数据到内存(如文件、网络)和从内存输出到外部。流是信息传输的抽象,分为字节流和字符流。字节流处理二进制数据,如InputStream和OutputStream,而字符流处理Unicode字符,如Reader和Writer。File对象用于文件和目录操作,Path对象简化了路径处理。ZipInputStream和ZipOutputStream则用于读写zip文件。