java基础面试题

文章目录

1.解释下什么是面向对象?面向对象和面向过程的区别?

面向对象是基于面向过程的一种编程思想,万物皆对象的思想
对象为中心,消息为驱动
区别:
  (1) 思路不同 面向过程是 面向实现功能函数开发为主, 面向对象是先抽象出类,属性和方法 然后通过实例化类,执行方法完成功能
  (2) 封装性: 都具有封装性,但是面向过程是封装功能,面向对象是数据和功能
  (3) 继承和多态是面向对象独有,也是优势
继承:通过继承,可以在无需重新编写原有类的情况下,对原有类的功能进行扩展
多态:多态指的是在一个类中定义的属性和功能被其他类继承后,当把子类对象直接赋值给父类引用变量时,相同引用类型的变量调用同一个方法所呈现出的多种不同行为特性。

2.面向对象的三大特性?分别解释下?

封装:就是将数据和操作数据的方法封装起来,只能用规定的接口来访问
继承:父类提供继承信息,子类得到其中信息
多态:多态分为编程时多态(重载)和运行时多态(重写),实现多态两个要求,一子类继承并且重写父类的方法,第二个是父类类型来引用子类型对象,这样同样的引用调用同样的方法就会根据子类对象的不同表现不同

补充 ,子类拥有父类的所有属性和方法 包括(私有属性和私有方法)但是无法访问(私有属性和私有方法),只是拥有,因为在一个子类被创建的时候,首先先在内存中创建一个父类对象 ,然后在父类对象外部放一个子类对象独有属性,二者构成子类对象
 (2)子类可以拥有自己属性和方法;
 (3)子类可以用自己的方式实现父类的方法。(重写)

3.重载和重写的区别?

  (1)重载 : 编译时多态 ,同一个类中同名的方法具有不同的参数列表,不能根据返回类型进行区分 (因为:函数调用时不能指定类型信息 ,编译器不知道你要调用哪个函数 )

  (2)重写(又名覆盖):运行时多态 ,子类与父类之间,子类重写父类方法具有相同的返回类型,更好的访问权限

4.Java 中是否可以重写一个 private 或者 static 方法?

  Java中的static方法不能被覆盖 因为方法覆盖是基于运行时动态绑定的 ,而static方法是编译时静态绑定的。static方法跟类的任何实例都不相关 所以概念上不适用
  Java中也不可以覆盖private的方法 因为private修饰的变量和方法只能在当前类中使用,如果是其他的类继承当前类是不能访问到private变量和方法,所以也不能覆盖

静态方法补充:静态方法可以被继承,但是不能重写。 如果父类和子类中存在同样名称和参数的静态方法,那么该子类的方法,那么该子类的方法会把原来继承过来的父类方法隐藏而不是重写。通俗来说就是父类方法和子类方法是两个没有关系的方法 ,具体调用哪一个方法看是哪个对象引用,这样的父子类方法也不在存在多态的性质

  

5. JDK、JRE、JVM 三者之间的关系?

JDK (Java Development Kit)Java开发工具包,是整个Java的核心,包括了Java运行环境JRE,Java工具和Java基础类库
JRE(Java Runtime Environmen) : Java的运行环境 包括JVM标准实现以交Java核心类库
JVM (Java VIrtual Macheine) : java虚拟机 ,是整个Java实现跨平台的最核心的部分 ,能够运行以Java语言写作的软件程序 。所有Java程序都是编译cheng.class的类文件 ,这种类文件可以在虚拟机上执行

什么是构造方法 ?

给对应对象初始化。
1、构造方法时-种特殊的方法,作用:给对应对象初始化。
特殊在哪?
1构造函数的函数名与类名- 致。
2默认构造函数没有参数,没有返回值。构造函数在函数体内也没有内容。不能声明void,访问权限可以为任意,但是一般情况下使用
public方法权限,构造方法中的参数可以根据需要自行定义,参数的不同的构造方法构成重载。


public class B extends A
public B()} /无参的公有构造方法
public B(int i){} I/参数类型为int的公有构造方法
public B(int i,double j,char f){} /参数类型为in和double,char的公有构造方法
}

6. 构造方法有哪些特性?

(1)名字和类名相同
(2) 没有返回值 ,但是不能用void声明构造函数
(3)成类的对象时自动执行,无需调用

7.在 Java 中定义一个不做事且没有参数的构造方法有什么作用?

  Java 程序在执行子类的构造方法之前,如果没有用 super() 来调用父类特定的构造方法,则会调用父类中“没有参数的构造方法”。
  因此,如果父类中只定义了有参数的构造方法,而在子类的构造方法中又没有用 super() 来调用父类中特定的构造方法,则编译时将发生错误,因为 Java 程序在父类中找不到没有参数的构造方法可供执行。
  解决办法是:在父类里加上一个不做事且没有参数的构造方法。或者将父类的有参构造方法补充完

   补充
  子类在实例化对象时候,必定会调用自己的构造方法,在自己的构造方法中又必定会调用父类的构造方法(默认调用父类的无参构造方法)。
   如果父类没有构造方法,就不会报错,系统默认送一个无参构造方法
如果父类自己有一个有参就不会送了,即上面的问题,父类有有参构造方法,如果没有用super就会报编译的错。
  因此这个无参不做事且无参的构造方法 就是为了子类继承父类的时候能够默认调用无参构造方法顺利实例化父类对象,而不产生编译上的错误。
调用子类构造方法之前会先调用父类的无参构造方法,目的是为了帮助子类完成初始化工作。

在这里插入图片描述
如图所示,这是通过反编译看出的

8.Java中创建对象的几种方式?

  1. new关键字
    //Person01 person = new Person01();
  2. Class.newInstance (Constructor.newInstance)不用下面的前提
    运用反射创建对象的最常用的方法 有一个前提类得是public的无参构造方法 ,也就是要有无参构造器
    //Person01 person = Person01.class.newInstance();
  3. 使用 Clone() 方法;
      无论何时我们调用一个对象的clone方法,JVM就会创建一个新的对象,将前面的对象的内容全部拷贝进去,用clone方法创建对象并不会调用任何构造函数。
      要使用clone方法,我们必须先实现Cloneable接口并复写Object的clone方法(因为Object的这个方法是protected的,若不复写,外部也调用不了)。
public class Person implements Cloneable {
	// 访问权限写为public,并且返回值写为person
    @Override
    public Person clone() throws CloneNotSupportedException {
        return (Person) super.clone();
    }
}
 
public class Main {
 
    public static void main(String[] args) throws Exception {
        Person person = new Person("fsx", 18);
        Object clone = person.clone();
 
        System.out.println(person);
        System.out.println(clone);
        System.out.println(person == clone); //false
    }
}
  1. 反序列化
        String filePath = "C:\\";
	Person01 person = (Person01) inputStream.readObject();
 try {
		  //序列化过程
          ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream(filePath));
            objectOutputStream.writeObject(new Person01("序列化,后边要进行反序列化了", 123456));
            objectOutputStream.close();
 
            //反序列化过程
			ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream(filePath));
			Person01 person = (Person01) inputStream.readObject();
			inputStream.close();

  当我们序列化和反序列化1一个对象,JVM会给我们创建一个单独的对象。
  在反序列化时候,JVM创建对象并不会调用任何构造函数。
  为了反序列化一个对象,我们需要让我们的Person01类实现Serializable接口。

第9题的前置知识点抽象类和接口介绍

抽象类
  抽象类是类和类之间的共同特征,将这些共同特征进一步形成抽象类,由于类本身不存在,所以抽象类无法创建对象。
  类到对象是实例化,对象到类是抽象
  抽象方法不能被 final 修饰,因为抽象方法就是被子类实现的

  抽象的方法只需在抽象类中,提供声明,不需要实现
  如果一个类中含有抽象方法,那么这个类必须定义成抽象类。抽象类中不一定有抽象方法,抽象方法必须出现在抽象类中
  final和abstract不能同时同时使用,这两个关键字是对立的
  f一个非抽象的类,继承抽象类,必须将抽象类中的抽象方法进行覆盖/重写/实现
  抽象类无法实例化,无法创建对象,抽象类是被子类来继承的

public class AbstractTest01 {
    public static void main(String[] args) {
        new a();//错误,不可创建对象
    }
}
//定义一个抽象类
abstract  class a{
    
}
//不是抽象类可以继承抽象类并且实例化
class b extends a{
}

//抽象子类可以继承抽象类
abstract class c extends a{
}

  抽象类无法实例化,但抽象类有构造函数可以供子类使用
  如果抽象类定义了有参构造函数而没有无参构造函数,子类定义找不到无参构造函数会出错,抽象类无法实例化调用对象(new对象),==但是有构造函数可以给子类使用.

详细参展第7个问题

abstract  class a{
    
}
class b extends a{
	public b(){
		super();
	}
}

抽象方法表示没有实现的方法,没有方法体的方法

接口

  接口是特殊的抽象类,类与类是继承extends,类与接口是实现implements,其实都是继承

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

  (1)抽象类能定义构造函数 ,接口不能定义构造函数;
  (2)抽象类可以有抽象方法 和具体方法 而接口只能有抽象方法(public abstract)
  (3)抽象类的成员权限可以的public,默认,protected(抽象类中的抽象方法就是为了重写,所以不能被private修饰),而接口中的成员只可以是public(方法默认:public abstract ,衬衣变量默认:public static final);
   (4)抽象类可以包含静态方法 ,而接口不能包含静态方法;

JDK1.8的新特性:
   允许接口中有具体实现的方法 ,使用default 修饰,这类方法就是默认方法。
2.接口可以包含静态方法,在jdk1.8之前不行,之前接口不行实现方法,现在可以实现了,但是只能直接用接口调用静态方法 ,而且仍然不可以包含静态代码.

  • 接口是一种“引用数据类型”,完全抽象的,支持多继承,且一个接口可以继承多个接口,只有常量+抽象方法
  • 所有的元素都是public修饰的,抽象方法的public abstract可以省略,常量的public static final可以省略,方法不能有方法体
[修饰符列表] interface 接口名{}

   支持多继承,且一个接口可以继承多个接口
   每一个interface 都会生成一个class后缀名的文件

接口中的方法是抽象,所以不能有方法体
定义抽象方法的时候可以省略修饰符public abstract

interface a{
	int sum(int a,intb);//可以省略,默认系统会给你加上
	void dosome();//可以执行通过
	void dosome(){};//不可以执行通过,不能有方法体
}

常量的定义结合final 是public static final可以省略

public class text1 {
    public static void main(String[] args) {
    	//定义输出的时候 需要用类名加变量 也就是a.i
    	//而且不允许在修改常量
	}
}
interface a{
	public static final int i=100;
	int z=1;//z也是一个常量而不是变量 不能修改
}

总结

  • 抽象类是半抽象的,有构造方法,只允许出现常量和抽象方法。类和类之间只能单继承,一个抽象类只能继承一个类(单继承)。

  • 接口是完全抽象的,没有构造方法,接口和接口之间支持多继承,一个类可以同时实现多个接口

10 静态变量和实例变量区别?

静态变量:是被static修饰的变量 ,也称为类变量,他属于类,一个静态变量在内存中只有一个拷贝,可以实现多个对象共享一个内存
实例变量 : 属于某一个实例,需要先创建对象 ,然后通过对象方法
区别
静态变量属于类不是对象,通过类的名字就能调用静态变量
实例变量属于类的对象

前置知识
包装类

Java语言是一个面向对象的语言,但是Java中的基本数据类型却是不面向对象的。基本类型的数据不具备"对象"的特性(没有成员变量和成员方法可以调用),因此,java为每种数据类型分别设计了对应的类,即包装类。

包装类的特点:

(1)所有包装类都是final类型,因此不能创建他们的子类。
(2)包装类是不可变类,一个包装类的对象自创建后,他所包含的基本类型数据就不能被改变。

11. Integer 和 int的区别 ?

(1)int是基本类型 ,Integer是 java为int类型 提供的封装类
(2)int变量默认是0,Integer变量的默认值为null 说明 Integer 能区分有无赋值和值为零的区别
(3)Integer 变量必须实例化后才可以使用 ,而int不需要
延伸
  1.由于Integer 变量实际上是对一个Integer对象的引用,所以两个通过new参数的Integer 变量不等 ,因为内存地址不同
  2.Integer 变量和int变量比较只比较值,包装类Integer和基本数据类型int类型比较,java会自动拆包,实际上就是两个int比较
  3.new和非new生成的Integer 变量 比较 是false ,因为非new生成是指向常量池的对象 ,而bew Integer() 生成的变量指向堆中新建的对象,二者的内存地址不同
  4.两个非new生成的Integer对象进行比较, 区间 [-128, 127] 之间,则比较结果为 true,否则为 false。
  Java 在编译 Integer i = 100 时,会编译成 Integer i = Integer.valueOf(100),
  Java 对于 [-128, 127] 之间的数会进行缓存,

12 装箱和拆箱的区别

自动装箱 就是 基本数据类型到包装类 如: int->integer 反之就是自动拆箱

  装箱方法
(1)使用Integer 的类构造方法

  • public Integer(int value):将int类型转换为Integer类。
  • public Integer(String s):将String类型转换为Integer类。
    (2)方法2:使用Integer类内部的valueOf方法
  • public static Integer valueOf(int i):将int类型转换为Integer类。

Integer i3 = Integer.valueOf(30);

(3)方法3:自动装箱的方法:
  编译器自动执行了valueOf方法。

Integer ii = 100;//编译器自动执行了Integer ii = Integer.valueOf(100)

  面试题

public static void main(String[] args)
	{
		Integer i1 = new Integer(127);
		Integer i2 = new Integer(127);
		System.out.println(i1 == i2);//false
		System.out.println(i1.equals(i2));//true重写了equals方法,比较的内容
		
		
		Integer i3 = new Integer(128);
		Integer i4 = new Integer(128);
		System.out.println(i3 == i4);//false
		System.out.println(i3.equals(i4));//true
	
		Integer i5 = 128;
		Integer i6 = 128;
		System.out.println(i5 == i6);//false
		System.out.println(i5.equals(i6));//true
		
		Integer i7 = 127;
		Integer i8 = 127;
		System.out.println(i7 == i8);//true
		System.out.println(i7.equals(i8));//true

总结
new 是创建一个新空间 , 所以 ==都不相等 ,而直接赋值会自动装箱 通过源码得知 -128-127这个范围之间不会创建新的空间 超出范围会创建空间

  拆箱方法

  1. 调用包装类的intValue()方法
    public int intValue():以 int 类型返回该 Integer 的值。
  2. 通过自动拆箱:
     编译器自动执行了valueOf方法

13、final、finally、finalize 的区别

final: 用于声明属性,方法和类,分别表示属性不可变,方法不可覆盖,被其修饰的类是不可继承;(关键字)
finally :异常结构的一部分,表示总是执行 (区块标志)
finallize:Object类的一个方法 在垃圾回收时会调用被回收对象的finallize (方法)

作用不同:
(1)final用于标识常量的关键字,final标识的关键字存储在常量池中,
(2) finalize()方法在Object 中进行了定义,用于在对象“消失”时,由JVM进行调用用于对对象进行垃圾回收
(3)finally 用于表示代码块 ,与try{}进行配合 该代码块必定执行
注意: finally 中不要有return,否则程序会提前退出,这样try或者catch 中的return 就不会返回。

14 == 和 equals 的区别?

简单来说 == 是判断两个变量或者实例是不是指向同一个内存空间,equals是判断两个变量或者实例所指向的内存空间是不是相同的

==是对内存地址进行比较,equals是对字符串的内容进行比较

==指引用是否相同 ,equals指的是值是否相同

  详细理解 ==比较的是变量(栈)内存中存放的对象的(堆)内存地址,判断是不是指向同一个对象
  也可以这么理解: String str = “hello”; 先在内存中找是不是有"hello"这个对象,如果有,就让str指向那个"hello".
  如果内存里没有"hello",就创建一个新的对象保存"hello". String str=new String (“hello”) 就是不管内存里是不是已经有"hello"这个对象,都新建一个对象保存"hello"。

15.两个对象的 hashCode() 相同,则 equals() 也一定为 true 吗?

  两个对象的hashCode()相同只能是他们的哈希值相同不一定代表两个键相同

16.为什么重写 equals() 就一定要重写 hashCode() 方法?

   前提是你要用到java集合 例如Hash Map 和HashSet ,即Java官方就建议我们重写equals 就一定要重写hashCode()
  第二个是可以减小开销 ,就是方便定位到对象的存储位置 ,先判断hashCode相同吗
  hashCode()与equals()的相关规定
  如果对象相同那么hashCode一定相同的,对两个对象分别调用equals方法都是true
  两个hashCode相同,两个对象不一定相同 ,即 equals方法覆盖,则hashCide也需要重写
  hashCode() 的默认行为是对堆上的对象产生独特值。如果没有重写 hashCode(),则该 class 的两个对象无论如何都不会相等(即使这两个对象指向相同的数据)。 引用问题

17Java 中的参数传递时传值呢?还是传引用?

Java 的参数是以值传递的形式传入方法中,而不是引用传递。
参数是基本类型: 方法不能改变一个基本数据类型的参数 
参数为引用类型:方法修改参数所指向对象的值,也就是拷贝了引用的值罢了,能修改的原因是因为他们同时指向一个对象,但是这仍然是按值调用而不是引用调用

  

18如何实现对象的克隆?

JAVA对象克隆共有两种形式,三种方法

浅克隆

调用clone方法
深克隆

递归调用clone方法
序列化对象
(1)实现Cloneable接口并且重写Object类中的clone()方法
(2)实现Serializable接口,通过对象的序列化和反序列化实现克隆,可以真正的深克隆

19.深克隆和浅克隆的区别?

数据类型分为值类型和引用类型 深浅拷贝区别在于是否支持引用类型的成员变量的复制
也就是说对于值类型的数据类型,二者是一样的,对于引用类型来说浅拷贝调用是同一地址
而深拷贝是将成员变量 可复制化 ,修改clone方法

20.什么是 Java 的序列化,如何实现 Java 的序列化?

   对象序列化 就是 对象到 ->字节流 保存到磁盘文件 或者网络发送到任何其他程序 反之为反序列化 ,而创建的字节流是为了兼容 为了跨平台传递读写操作的问题
  序列化实现 : 将需要序列化的类实现 Serializable接口,该接口没有需要实现的方法,只是用于标注该对象是可被序列化的,然后使用一个输出流(FileOutputStream) 来构造一个ObjectOutputStream 对象 ,接着使用这个对象的writeObject(Object obj) 方法 可以将参数为obj对象写出,要恢复的话就是用输入流 。

21.什么情况下需要序列化?

(1)当你想把内存中的对象状体保存到一个文件中或者数据库中的时候
(2)当你想用套接字在网络传送对象的时候
(3)当年想通过RMI传输对象的时候

22.Java 的泛型是如何工作的 ? 什么是类型擦除 ?

  泛型是通过类型擦除来实现的,编译器在编译时擦除了所有类型相关的信息,所以在运行时不存在任何类型相关的信息。
  类型擦除:泛型信息只存在于代码编译阶段,在进入 JVM 之前,与泛型相关的信息会被擦除掉,专业术语叫做类型擦除。
  在泛型类被类型擦除的时候,之前泛型类中的类型参数部分如果没有指定上限,如 < T > 则会被转译成普通的 Object 类型,如果指定了上限如 < T extends String > 则类型参数就被替换成类型上限。

23. Java 中的反射是什么意思?有哪些应用场景?

  反射就是指程序在运行的时候可以知道一个类的自身信息。
  反射就是把java类中的各种成分映射成一个个的Java对象
  个类有:成员变量、方法、构造方法、包等等信息,利用反射技术可以对一个类进行 解剖,把个个 组成部分映射成一个个对象。

  (1)Field :可以使用 get() 和 set() 方法读取和修改 Field 对象关联的字段;
  (2)Method :可以使用 invoke() 方法调用与 Method 对象关联的方法;
  (3)Constructor :可以用 Constructor 创建新的对象。

应用举例:工厂模式,使用反射机制,根据全限定类名获得某个类的 Class 实例。

24.反射的优缺点?

优点:
  运行期类型的判断,class.forName() 动态加载类,提高代码的灵活度;

缺点:
  (1)性能开销 :反射涉及了动态类型的解析,所以 JVM 无法对这些代码进行优化。
  (2)安全限制 :使用反射技术要求程序必须在一个没有安全限制的环境中运行。
3)内部暴露:由于反射允许代码执行一些在正常情况下不被允许的操作(比如:访问私有的属性和方法),所以使用反射可能会导致意料之外的副作用,这可能导致代码功能失调并破坏可移植性。

  

25.Java 中的动态代理是什么?有哪些应用?

**动态代理:**当想要给实现了某个接口的类中的方法,加一些额外的处理。比如说加日志,加事务等。可以给这个类创建一个代理,故名思议就是创建一个新的类,这个类不仅包含原来类方法的功能,而且还在原来的基础上添加了额外处理的新功能。这个代理类并不是定义好的,是动态生成的。具有解耦意义,灵活,扩展性强。
动态代理的应用: Spring 的 AOP 、加事务、加权限、加日志。

26.怎么实现动态代理?

  首先必须定义一个接口,还要有一个 InvocationHandler(将实现接口的类的对象传递给它)处理类。再有一个工具类 Proxy(习惯性将其称为代理类,因为调用它的 newInstance() 可以产生代理对象,其实它只是一个产生代理对象的工具类)。
  每一个动态代理类都必须要实现 InvocationHandler 这个接口,并且每个代理类的实例都关联到了一个 handler,当我们通过代理对象调用一个方法的时候,这个方法的调用就会被转发为由 InvocationHandler 这个接口的 invoke 方法来进行调用。我们来看看 InvocationHandler 这个接口的唯一一个方法 invoke 方法:

Object invoke(Object proxy, Method method, Object[] args) throws Throwable

proxy: 指代我们所代理的那个真实对象
method: 指代的是我们所要调用真实对象的某个方法的 Method 对象
args: 指代的是调用真实对象某个方法时接受的参数
Proxy 类的作用是动态创建一个代理对象的类。它提供了许多的方法,但是我们用的最多的就是

27.static 关键字的作用?

  (1)静态变量:类变量,类的所有实例都是共享静态变量,可以直接通过类名访问它,静态变量在内存中只存在一部分;
  (1)静态方法:方法在类加载的时候的存在了,它不依赖于任何实例,所以他必须实现,也就是说他不能是抽象方法。只能访问所属类的静态字段和静态方法,方法不能有this和super关键字
  (3)静态语句块:静态语句块在类初始化时运行一次;
  (4)静态内部类:非静态内部类依赖于外部类的实例,而静态内部类不需要。静态内部类不能访问外部类的非静态的变量和方法;
  (5)初始化顺序:静态变量和静态语句块优先于实例变量和普通语句块,静态变量和静态语句块的初始化顺序取决于它们在代码中的顺序。

public static String staticField = "静态变量";
  static {    
    System.out.println("静态语句块");   ---- 1
    }
    public String field = "实例变量";       ---- 2

    {
        System.out.println("普通语句块");  ----3
      }
      // 最后才是构造函数的初始化
      public InitialOrderTest() {
      System.out.println("构造函数");    ----4
      }

28.super 关键字的作用?

(1)访问父类的构造函数 : 可以使用super()函数访问父类的构造函数从而委托父类完成一些初始化工作。
(2)访问父类的成员:如果子类重写了父类的某个方法,可以通过使用 super 关键字来引用父类的方法实现。
(3)this和super不能同时出现在一个构造函数里面,因为this必然会调用其他函数的构造函数,其它的构造函数必然也会有 super 语句的存在,所以在同一个构造函数里面有相同的语句,就失去了语句的意义,编译器也不会通过。

29.字节和字符的区别?

字节是存储容量的基本单位
字符是数字、字母、汉字以及其他语言的各种符号
1 字节 = 8 个二进制单位,一个字符由一个字节或多个字节的二进制单位组成。

30.String 为什么要设计为不可变类?

三点
(1)字符串常量池的需要:字符串的常量池是Java堆内存中的一个特殊存在的储存区域,当创建一个String对象时,假如字符串已经存在常量池中,则不会创建一个新对象,而是引用已经存在的。
(2) 允许String 对象缓存HashCode:Java 中 String 对象的哈希码被频繁地使用, 比如在 HashMap 等容器中。字符串不变性保证了 hash 码的唯一性,因此可以放心地进行缓存。这也是一种性能优化手段,意味着不必每次都去计算新的哈希码;
(3) String 被许多的 Java 类(库)用来当做参数,例如:网络连接地址 URL、文件路径 path、还有反射机制所需要的 String 参数等, 假若 String 不是固定不变的,将会引起各种安全隐患。

31.String、StringBuilder、StringBuffer 的区别?

String:用于字符串操作,属于不可变类
StringBulider:与 StringBuffer 类似,都是字符串缓冲区,但线程不安全;
StringBuffer:也用于字符串操作,不同之处是 StringBuffer 属于可变类,对方法加了同步锁,线程安全

执行效率:StringBuilder > StringBuffer > String

  StringBuffer 是多线程操作字符串 ,而 StringBuilder 是单线程
  StringBuffer 对象没搞都有一定的缓冲区容量 当字符串大小没用有超过容量时,不会分配新容量
当字符串大小超过容量,会自动扩容

32.String 字符串修改实现的原理?

  当用 String 类型来对字符串进行修改时,其实现方法是首先创建一个 StringBuilder,其次调用 StringBuilder 的 append() 方法,最后调用 StringBuilder 的 toString() 方法把结果返回。

33.String str = “i” 与 String str = new String(“i”) 一样吗?

  不一样,因为内存的分配方式不一样。String str = “i” 的方式,Java 虚拟机会将其分配到常量池中;而 String str = new String(“i”) 则会被分到堆内存中。

  执行 String str3 = new String(“abc”) 的时候,JVM 会首先检查字符串常量池中是否已经存在“abc”字符串,如果已经存在,则不会在字符串常量池中再创建了;如果不存在,则就会在字符串常量池中创建 “abc” 字符串对象,然后再到堆内存中再创建一份字符串对象,把字符串常量池中的 “abc” 字符串内容拷贝到内存中的字符串对象中,然后返回堆内存中该字符串的内存地址,即栈内存中存储的地址是堆内存中对象的内存地址。String str4 = new String(“abc”) 是在堆内存中又创建了一个对象,
在这里插入图片描述

34.final 修饰 StringBuffer 后还可以 append 吗?

StringBuffer:线程安全的可变字符序列。一个类似于 String 的字符串缓冲区,但不能修改。虽然在任意时间点上它都包含某种特定的字符序列,但通过某些方法调用可以改变该序列的长度和内容。

  可以,如果首次创建对象,然后给该对象append,那么是可以的,因为final类型的数据对于引用不变是指地址不变,而不是内容;但是如果在重新new这个final类型的StringBuffer,这是错误的。
   StringBuffer 理解为缓冲区,不能通过赋值符号对其进行赋值,使用append进行值修改。没有创建新的对象。
   对其加以final修饰,fianl 修饰引用对象,代表引用对象不可变,StringBuffer 实现append()没有产生新的对象,所以可以。

35.Java 中的 IO 流的分类?说出几个你熟悉的实现类?

按照功能区分: 输入流(input),输出流(output)
按类型来分 :字节流 和 字符流。
字节流: I所有的文件都可以用字节流读取 nputStream /OutputStream FileInputStream /FileOutputStream
字符流:读取文本的时候使用字符流,InputStreamReader\OutputStreamWriter FileReader/FileWriter

36.字节流和字符流有什么区别?

字节流按照8位传输,按照字节为单位来输入输出数据,字符流按照16位1传输,以字符单位输入输出数据
但是不管文件读写还剩网络发送接收,信息的最小存储单元都是字节
第二点,传输通道的不同,字节流是直接和文件连接一条通道传输,字符流是将数据放在缓冲区,在连接关闭的时候才缓存才会传给文件,假如不关闭连接,缓存不会传输也就是文件没有数据

37.BIO、NIO、AIO有什么区别?

   首先先看一下分别是啥
   BIO: Block IO同步阻塞式IO,就是我们平常使用的传统IO,他的特点是模式简单使用方便,并发处理能力低,连接数目不高的时候,有优势他会必须阻塞在一个线程内等待完成,可以让每一个连接都专注直接的IO,线程池本身就是一个天然的漏斗,可以缓冲一些系统处理不了的连接或请求。如果是大量连接的传统的BIO是无能为力。
   NIO:New IO 同步非阻塞 IO,是传统 IO 的升级,客户端和服务器端通过 Channel(通道)通讯,实现了多路复用。NIO 中的 N 可以理解为 Non-blocking,不单纯是 New。它支持面向缓冲的,基于通道的 I/O 操作方法。NIO 提供了与传统BIO模型中的 Socket 和 ServerSocket 相对应的 SocketChannel 和 ServerSocketChannel 两种不同的套接字通道实现,两种通道都支持阻塞和非阻塞两种模式。阻塞模式使用就像传统中的支持一样,比较简单,但是性能和可靠性都不好;非阻塞模式正好与之相反。对于低负载、低并发的应用程序,可以使用同步阻塞 I/O 来提升开发速率和更好的维护性;对于高负载、高并发的(网络)应用,应使用 NIO 的非阻塞模式来开发。
   AIO:Asynchronous IO 是 NIO 的升级,也叫 NIO2,实现了异步非堵塞 IO ,异步 IO 的操作基于事件和回调机制。也就是应用操作之后会直接返回,不会堵塞在那里,当后台处理完成,操作系统会通知相应的线程进行后续的操作。

区别
BIO : 同步阻塞 传统IO,模式简单并发处理能力低。
NIO: 同步非阻塞 传统IO升级 客户端和服务端通过Channel (通道)通讯,多路复用
AIO:异步非堵塞 IO Asynchronous 非同步 IO NIO升级,操作基于事件和回调机制。

  • 4
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Red-P

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值