进大厂背过的Java面试题系列-11

1.字节流与字符流的区别

        

1)字节流在操作的时候本身是不会用到缓冲区(内存)的,是与文件本身直接操作的,而字符流在操作的时候是使用到缓冲区的。

2)字节流在操作文件时,即使不关闭资源(close方法),文件也能输出,但是如果字符流不使用close方法的话,则不会输出任何内容,说明字符流用的是缓冲区,并且可以使用flush方法强制进行刷新缓冲区,这时才能在不close的情况下输出内容

3)Reader类的read()方法返回类型为int :作为整数读取的字符(占两个字节共16位),范围在 0 到 65535 之间 (0x00-0xffff),如果已到达流的末尾,则返回 -1

inputStream的read()虽然也返回int,但由于此类是面向字节流的,一个字节占8个位,所以返回 0 到 255 范围内的 int 字节值。如果因为已经到达流末尾而没有可用的字节,则返回值 -1。因此对于不能用0-255来表示的值就得用字符流来读取!比如说汉字。

4)字节流与字符流主要的区别是他们的的处理方式

字节流以stream结尾;而字符流以reader和writer结尾;

字节流:处理字节和字节数组或二进制对象;

字符流:处理字符、字符数组或字符串。

2.final、finalize()、finally的区别

性质不同

1) final为关键字;

2) finalize()为方法;

3) finally为区块标志,用于try语句中;

作用不同

1) final为用于标识常量的关键字,final标识的关键字存储在常量池中(在这里final常量的具体用法将在下面进行介绍);

2)finalize()方法在Object中进行了定义,用于在对象“消失”时,由JVM进行调用用于对对象进行垃圾回收,类似于C++中的析构函数;用户自定义时,用于释放对象占用的资源(比如进行I/0操作);

3)finally{}用于标识代码块,与try{}进行配合,不论try中的代码执行完或没有执行完(这里指有异常),该代码块之中的程序必定会进行;

3.线程同步的方法

1)synchronized关键字

  1. Synchronized修饰整个方法

例:
public synchronized void set(){}

注意:synchronized也可以修饰静态方法,如果此时调用这个方法,将会锁住整个类。

同步是一种高开销的操作,因此,应该尽量减少同步的内容。一般情况下不需要同步整个方法,只需同步对应的代码块即可。

2)Lock

Lock与Synchronized比较起来,Lock的优点还是比较多的。如果一个代码块被synchronized关键字修饰,当一个线程执行该代码块时,其他的线程便只能一直等待直到占有锁的线程释放锁。那么,什么情况下占有锁的线程会释放锁呢 ?

  1. 占有锁的线程执行完了该代码块,然后释放。

  2. 占有锁的线程执行发生了异常,此时JVM会让线程自动释放锁。

  3. 占有锁的线程进入waiting状态从而释放锁,

既然synchronized如此方便,可以轻松实现对资源的同步互斥访问。那么,为什么还需要Lock呢 ?考虑以下3种情况:

Case 1 :

在使用synchronized关键字时,假如占有锁的线程由于要等待IO或者其他原因(比如调用sleep()方法)被阻塞了,但是又没有释放锁,那么其他的线程就只能一直等待。

Case 2:

多个线程读写文件时,读和写操作会发生冲突,写和写操作也会发生冲突,但是,读和读操作不会发生冲突。所以使用synchronized关键字就会导致一个问题:无法实现多个线程同步进行读操作。这就会极大地影响程序的执行效率。

Case 3:

我们可以通过Lock得知线程有没有成功获取锁,但是synchronized无法做到。

以上三个Case中所描述的问题,Lock都可以解决

lock.lock() 加锁;lock.unlock() 释放锁。

public class Count{
Lock lock = new ReentrantLock();
public void save(){
for(int i=0;i<10;i++)
{ count++;
System.out.println("count:"+count);
}
lock.unlock();
}
}

3)volatile关键字

volatile是变量修饰符,其修饰的变量具有可见性。

可见性就是说一旦某个线程修改了被volatile修饰的变量,它会保证修改的值会立即被更新到主存,当有其他线程需要读取的时候,可以立即获取修改之后的值。

在Java中为了加快程序的运行效率,对一些变量的操作通常是在该线程的寄存器或CPU缓存上进行的,之后才会同步到主存中,而加了volatile修饰符的变量则是直接读写主存。

 volatile可以禁止指令重排

指令重排是指编译器或者CPU为了提高程序的运行效率,可能会对输入的代码进行优化,它不保证各个语句的执行顺序同代码中的顺序一致,但是它会保证程序最终执行结果和代码中的执行结果是一致的,应用条件是单线程条件,对于并发多线程的场景下,指令重排会产生不确定的结果。

4.&和&&的区别

    &是位运算符。&&是布尔逻辑运算符,在进行逻辑判断时用&处理的前面为false后面的内容仍需处理,用&&处理的前面为false不再处理后面的内容。

5.如果对象的引用被置为null,垃圾收集器是否会立即释放对象占用的内存?

不会,在下一个垃圾回收周期中,这个对象将是可被回收的。

6、面向对象和面向过程的区别

面向过程:面向过程性能比面向对象高。因为类调用时需要实例化,开销比较大,比较消耗资源,所以当性能是最重要的考量因素的时候,比如单片机、嵌入式开发、Linux/Unix等一般采用面向过程开发。但是,面向过程没有面向对象易维护、易复用、易扩展。

面向对象:面向对象易维护、易复用、易扩展。因为面向对象有封装、继承、多态性的特性,所以可以设计出低耦合的系统,使系统更加灵活、更加易于维护。但是,面向对象性能比面向过程低。

7、Java 语言有哪些特点?

简单易学;

平台无关性( Java 虚拟机实现平台无关性);

可靠性;

安全性;

支持多线程( C++ 语言没有内置的多线程机制,因此必须调用操作系统的多线程功能来进行多线程程序设计,而 Java 语言却提供了多线程支持);

支持网络编程并且很方便( Java 语言诞生本身就是为简化网络编程设计的,因此 Java 语言不仅支持网络编程而且很方便);

编译与解释并存;

8.Java和C++的区别?

都是面向对象的语言,都支持封装、继承和多态。

Java 不提供指针来直接访问内存,程序内存更加安全。

 Java 的类是单继承的,C++ 支持多重继承;虽然 Java 的类不可以多继承,但是接口可以多继承。

Java 有自动内存管理机制,不需要程序员手动释放无用内存。

9.字符型常量和字符串常量的区别?

1)形式上:字符常量是单引号引起的一个字符;字符串常量是双引号引起的若干个字符。

2)含义上:字符常量相当于一个整型值( ASCII 值),可以参加表达式运算; 字符串常量代表一个地址值(该字符串在内存中存放位置)。

3)占内存大小:字符常量只占2个字节;字符串常量占若干个字节(至少一个字符结束标志)。(注意:char在Java中占两个字节)

一个字节(Byte)占8位(8bit)

10大基本数据类型及其字节数?

byte:1字节    short:2字节

int:4字节    long:8字节

float:4字节精确到7位有效数字    double:8字节

chr:2字节    boolean:1位

引用类型:4字节,1个字节表示8位

11.String为什么是不可变的?

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

String是被声明为final class,除了hash这个属性其它属性都 声明为final。因为它的不可变性,所以例如拼接字符串时候会产生很多无用的中间对象,如果频繁的进行这样的操作对性能有所影响。

StringBuffer就是为了解决大量拼接字符串时产生很多中间对象问题而提供的一个类,提供append和add方法,可以将字符串添加到已有序列的末尾或指定位置,它的本质是一个线程安全的可修改的字符序列,把所有修改数据的方法都加上了synchronized。但是保证了线程安全是需要性能的代价的。

  在很多情况下我们的字符串拼接操作不需要线程安全,这时候StringBuilder登场了,StringBuilder是JDK1.5发布的,它和StringBuffer本质上没什么区别,就是去掉了保证线程安全的那部分,减少了开销。

StringBuffer 和 StringBuilder 二者都继承了 AbstractStringBuilder ,底层都是利用可修改的char数组(JDK 9 以后是 byte数组)。

操作少量的数据: 使用String

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

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

12.说说字符串常量池:

Java为了避免在一个系统中产生大量的String对象,引入了字符串常量池。

创建一个字符串时,首先会检查池中是否有值相同的字符串对象,如果有就直接返回引用,不会创建字符串对象;如果没有则新建字符串对象,返回对象引用,并且将新创建的对象放入池中。但是,通过new方法创建的String对象是不检查字符串常量池的,而是直接在堆中创建新对象,也不会把对象放入池中。上述原则只适用于直接给String对象引用赋值的情况。

String str1 = newString("a"); //不检查字符串常量池的
String str2 = "bb"; //检查字符串常量池的

String还提供了intern()方法。调用该方法时,如果字符串常量池中包括了一个等于此String对象的字符串(由equals方法确定),则返回池中的字符串的引用。否则,将此String对象添加到池中,并且返回此池中对象的引用。

  

13.int和Integer的区别?

int是基本数据类型,Integer是int的包装类就是将int类型包装成Object对象;

Integer变量必须实例化后才能使用;int变量不需要;

Integer实际是对象的引用,指向此new的Integer对象;int是直接存储数据值;

Integer的默认值是null;int的默认值是0。

深入:

 两个通过new生成的Integer变量永远是不相等的。因为new生成的是两个对象,其内存地址不同。

Integer与new Integer不会相等。因为非new生成的Integer变量指向的是java常量池中的对象,而new Integer()生成的变量指向堆中新建的对象,两者在内存中的地址不同。 两个都是非new出来的Integer,如果数在-128到127之间,则是true,否则为false。

java在编译Integer i = 127的时候,被翻译成 Integer i = Integer.valueOf(127); 

java API中对Integer类型的valueOf的定义如下,对于-128到127之间的数,会进行缓存,Integer i = 127时,会将127这个Integer对象进行缓存,下次再写Integer j = 127时,就会直接从缓存中取,就不会new了。

 Integer变量和int变量比较时,只要两个变量的值是相等的,则结果为true。(因为包装类Integer和基本数据类型int比较时,java会自动拆箱为int,然后进行比较,实际上就变为两个int变量的比较)

14.为什么有了int还要有设计Integer?

  对象封装有很多好处,可以把属性也就是数据跟处理这些数据的方法结合在一起,比如Integer就有parseInt()等方法来专门处理int型相关的数据。

另一个非常重要的原因就是在Java中绝大部分方法或类都是用来处理类类型对象的,如ArrayList集合类就只能以类作为他的存储对象,而这时如果想把一个int型的数据存入list是不可能的,必须把它包装成类,也就是Integer才能被List所接受。所以Integer的存在是很必要的。

 欢迎关注公众号:“Java元空间” ,回复“1”,获取大厂面试资料。这里有Java、SQL、大厂面试经验、面试题,欢迎大家关注交流。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值