java面试题(一)

1、线程和进程的区别?
线程是进程中的指令执行流的最小单位,是CPU调度的基本单位。
进程是指一个具有一定独立功能的程序。

2、线程有哪几个基本状态?它们之间如何转化?简述线程的生命周期
线程的状态:创建、就绪、运行、等待、阻塞、挂起、睡眠、等待、死亡
在这里插入图片描述
在这里插入图片描述
3、wait() 和 sleep() 方法的区别?
(1)sleep() 方法是 Thread 类中的方法,而 wait() 方法是 Object 类中的方法。
(2)sleep() 方法不会释放lock,但是 wait() 方法会释放,而且会加入到等待队列中。
(3)sleep() 方法不依赖于同步器 synchronized(),但是 wait() 方法 需要依赖 synchronized 关键字。

4、简要说明流的分类方式,其中字符流和字节流的主要区别是什么?
1、字节流 :字节读写, 字节流(ASCII)处理二进制文件。
可以传输音频,视频,图片,文本等,传输数据的基本单位为字节。
InputStream OutputStream

2、字符流:快读写 ,字符流(Unicode)处理文本文件。
只能传输纯文本, 传输数据的基本单位为字符 。
Writer Reader 一个字符等于2个字节
在这里插入图片描述
5、什么是反射?
Java的反射(reflection)机制是指在程序的运行状态中,可以构造任意一个类的对象,可以了解任意一个对象所属的类,可以了解任意一个类的成员变量和方法,可以调用任意一个对象的属性和方法。

6、抽象类(abstract class)和接口(interface)有什么区别?
抽象类(abstract class)含有abstract修饰符的class即为抽象类,abstract 类不能创建的实例对象。含有abstract方法的类必须定义为abstract class,abstract class类中的方法不必是抽象的。abstract class类中定义抽象方法必须在具体(Concrete)子类中实现,所以,不能有抽象构造方法或抽象静态方法。如果的子类没有实现抽象父类中的所有抽象方法,那么子类也必须定义为abstract类型。

接口(interface)可以说成是抽象类的一种特例,接口中的所有方法都必须是抽象的。接口中的方法定义默认为public abstract类型,接口中的成员变量类型默认为public static final。

两者区别:
(1)抽象类可以有构造方法,接口中不能有构造方法。
(2)抽象类中可以有普通成员变量,接口中没有普通成员变量
(3)抽象类中可以包含非抽象的普通方法,接口中的所有方法必须都是抽象的,不能有非抽象的普通方法。
(4)抽象类中的抽象方法的访问类型可以是public,protected和(默认类型,虽然eclipse下不报错,但应该也不行),但接口中的抽象方法只能是public类型的,并且默认即为public abstract类型。
(5)抽象类中可以包含静态方法,接口中不能包含静态方法
(6)抽象类和接口中都可以包含静态成员变量,抽象类中的静态成员变量的访问类型可以任意,但接口中定义的变量只能是public static final类型,并且默认即为public static final类型。
(7)一个类可以实现多个接口,但只能继承一个抽象类。

7、String和StringBuilder、StringBuffer的区别?

String的值是不可变的,即一旦一个String对象被创建以后,包含在这个对象中的字符序列是不可改变的,直至这个对象被销毁。,这就导致每次对String的操作都会生成新的String对象,不仅效率低下,而且浪费大量优先的内存空间。

StringBuffer是可变类,和线程安全的字符串操作类,任何对它指向的字符串的操作都不会产生新的对象。每个StringBuffer对象都有一定的缓冲区容量,当字符串大小没有超过容量时,不会分配新的容量,当字符串大小超过容量时,会自动增加容量。StringBuffer对象则代表一个字符序列可变的字符串,当一个StringBuffer被创建以后,通过StringBuffer提供的append()、insert()、reverse()、setCharAt()、setLength()等方法可以改变这个字符串对象的字符序列。一旦通过StringBuffer生成了最终想要的字符串,就可以调用它的toString()方法将其转换为一个String对象。StringBuffer对象是一个字符序列可变的字符串,它没有重新生成一个对象,而且在原来的对象中可以连接新的字符串。

StringBuilder类也代表可变字符串对象。实际上,StringBuilder和StringBuffer基本相似,两个类的构造器和方法也基本相同。不同的是:StringBuffer是线程安全的,而StringBuilder则没有实现线程安全功能,所以性能略高。

StringBuffer类中的方法都添加了synchronized关键字,也就是给这个方法添加了一个锁,用来保证线程安全。

String、StringBuffer、StringBuilder比较。

三者共同之处:都是final类,不允许被继承,主要是从性能和安全性上考虑的,因为这几个类都是经常被使用着,且考虑到防止其中的参数被参数修改影响到其他的应用。

(1)StringBuffer是线程安全,可以不需要额外的同步用于多线程中;
(2)StringBuilder是非同步,运行于多线程中就需要使用着单独同步处理,但是速度就比StringBuffer快多了;
(3)StringBuffer与StringBuilder两者共同之处:可以通过append、indert进行字符串的操作。
(4)String实现了三个接口:Serializable、Comparable、CarSequence
(5)StringBuilder只实现了两个接口Serializable、CharSequence,相比之下String的实例可以通过compareTo方法进行比较,其他两个不可以。
(6)运行速度。
执行速度由快到慢:StringBuilder > StringBuffer > String

8、重载(Overload)和重写(Override)的区别?

重载(overloading) 是在一个类里面,方法名字相同,而参数不同。返回类型可以相同也可以不同。

每个重载的方法(或者构造函数)都必须有一个独一无二的参数类型列表。

重写是子类对父类的允许访问的方法的实现过程进行重新编写, 返回值和形参都不能改变。即外壳不变,核心重写!

重写是发生在类的继承关系,或者类的实现关系中的,重写后的方法和原方法需要保持完全相同的返回值类型、方法名、参数个数以及参数类型,简单来说,就是子类重写的方法必须和父类保持完全一致

区别:

方法重载是一个类中定义了多个方法名相同,而他们的参数的数量不同或数量相同而类型和次序不同,则称为方法的重载(Overloading)。
方法重写是在子类存在方法与父类的方法的名字相同,而且参数的个数与类型一样,返回值也一样的方法,就称为重写(Overriding)。
方法重载是一个类的多态性表现,而方法重写是子类与父类的一种多态性表现。

9、java中的继承是单继承还是多继承?java支持多层继承吗?什么情况下普通子类必须重写父类的方法?
java中的继承是单继承,一个子类继承一个父类。不支持多继承。

(1)抽象类继承,如果子类也是一个抽象类,并不要求一定重写父类方法。如果子类不是抽象类,则要求子类一定要实现父类中的抽象方法。

(2)接口类继承。如果是一个子接口,可以扩展父接口的方法;如果是一个子抽象类,可以部分或全部实现父接口的方法;如果子类不是抽象类,则要求子类一定要实现父接口中定义的所有方法。

10、== 和 equals 的区别是什么?
“==”是运算符
①如果比较的对象是基本数据类型,则比较的是其存储的值是否相等;
②如果比较的是引用数据类型,则比较的是所指向对象的地址值是否相等(是否是同一个对象)。

equals是Object的方法,用来比较两个对象的地址值是否相等。
①equals 方法不能用于比较基本数据类型,如果没有对 equals 方法进行重写,则相当于“==”,比较的是引用类型的变量所指向的对象的地址值。
②一般情况下,类会重写equals方法用来比较两个对象的内容是否相等。比如String类中的equals()是被重写了,比较的是对象的值。

11、List、Set、Map三者的区别?
List

  1. List和数组类似,可以动态增长,根据实际存储的数据的长度自动增长List的长度。查找元素效率高,插入删除效率低,因为会引起其他元素位置改变;
  2. List接口是一个有序的 可以有重复元素的;
  3. 使用场景的选择:
    1. 对于需要快速插入、删除元素,则需使用LinkedList。
    2. 对于需要快速访问元素,则需使用ArrayList。
    3. List只能被一个线程操作,如果是多线程环境,List可能同时被多个线程操作,造成错误 考虑使用同步的类(如Vector)。

Set

  1. 不允许存储重复元素(根据重写hashCode()和equals()方法来判断是不是同一对象)。
  2. 没有索引,没有带索引的方法,所以也就不能使用普通for循环。

Map

  1. Map是键值对集合 以 key:value保存(是成对出现的),其中的key是唯一的标识;
  2. HashMap:基于 hash 表的 Map 接口实现,非线程安全,高效,支持 null 值和 null

List、Set和Map集合的区别?

(1)List和Set是存储单列数据的集合;

(2)Map是存储键值对这样的双列数据的集合;

(3)List中存储的数据是有顺序的,并且值允许重复;

(4)Map中存储的数据是无序的,它的键是不允许重复的,但是值是允许重复的;

(5)Set中存储的数据是无顺序的,并且不允许重复,但元素在集合中的位置是由元素的hashcode决定,即位置是固定的(Set集合是根据hashcode来进行数据存储的,所以位置是固定的,但是这个位置不是用户可以控制的,所以对于用户来说set中的元素还是无序的)

12、类加载的过程?
一个类从被加载到虚拟机内存中开始,到从内存中卸载,整个生命周期需要经过七个阶段:加载 (Loading)、验证(Verification)、准备(Preparation)、解析(Resolution)、初始化 (Initialization)、使用(Using)和卸载(Unloading),其中验证、准备、解析三个部分统称为连接(Linking)。
在这里插入图片描述
13、为什么不能根据返回类型来区分重载?
在调用目标方法时,是无法指定返回值类型信息的,这个时候编译器并不知道你要调用哪个函数。
在Java语言中,要重载一个方法,除了要与原方法具有相同的简单名称之外,还要求必须拥有一个与原方法不同的特征签名;
特征签名就是一个方法中各个参数在常量池中的字段符号引用的集合,也就是因为返回值不会包含在特征签名之中,因此Java语言里面是无法仅仅依靠返回值的不同来对一个已有方法进行重载。

14、深拷贝和浅拷贝区别是什么?
深拷贝和浅拷贝是指在赋值一个对象时,拷贝的深度不同。

在进行深拷贝时,会拷贝所有的属性,并且如果这些属性是对象,也会对这些对象进行深拷贝,直到最底层的基本数据类型为止。这意味着,对于深拷贝后的对象,即使原对象的属性值发生了变化,深拷贝后的对象的属性值也不会受到影响。

浅拷贝只会拷贝对象的第一层属性,如果这些属性是对象,则不会对这些对象进行拷贝,而是直接复制对象的引用。这意味着,对于浅拷贝后的对象,如果原对象的属性值发生了变化,浅拷贝后的对象的属性值也会跟着发生变化。

15、BIO、NIO、AIO 有什么区别?
BIO:Block IO同步阻塞式IO,就是我们平常使用的传统IO,它的特点是模式简单使用方便,并发处理能力低。

NIO:Non IO同步非阻塞IO,是传统IO的升级,客户端和服务器端通过Channel(通道)通讯,实现了多路复用。

AIO:Asynchronous IO是NIO的升级,也叫NIO2,实现了异步非堵塞IO,异步IO的操作基于事件和回调机制。

BIO是一个连接一个线程。

NIO是一个请求一个线程。

AIO是一个有效请求一个线程。

BIO:同步并阻塞,服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销,当然可以通过线程池机制改善。

NIO:同步非阻塞,服务器实现模式为一个请求一个线程,即客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有I/O请求时才启动一个线程进行处理。

AIO:异步非阻塞,服务器实现模式为一个有效请求一个线程,客户端的I/O请求都是由OS先完成了再通知服务器应用去启动线程进行处理。

在这里插入图片描述

16、Java 中的 final 关键字有哪些用法?

1、修饰数据

在编写程序时,我们经常需要说明一个数据是不可变的,我们成为常量。在java中,用final关键字修饰的变量,只能进行一次赋值操作,并且在生存期内不可以改变它的值。更重要的是,final会告诉编译器,这个数据是不会修改的,那么编译器就可能会在编译时期就对该数据进行替换甚至执行计算,这样可以对我们的程序起到一点优化。不过在针对基本类型和引用类型时,final关键字的效果存在细微差别。

2、修饰方法参数

前面我们可以看到,如果变量是我们自己创建的,那么使用final修饰表示我们只会给它赋值一次且不会改变变量的值。那么如果变量是作为参数传入的,我们怎么保证它的值不会改变呢?这就用到了final的第二种用法,即在我们编写方法时,可以在参数前面添加final关键字,它表示在整个方法中,我们不会(实际上是不能)改变

3、修饰方法

第三种方式,即用final关键字修饰方法,它表示该方法不能被覆盖。这种使用方式主要是从设计的角度考虑,即明确告诉其他可能会继承该类的程序员,不希望他们去覆盖这个方法。这种方式我们很容易理解,然而,关于private和final关键字还有一点联系,这就是类中所有的private方法都隐式地指定为是final的,由于无法在类外使用private方法,所以也就无法覆盖它。

4、修饰类

了解了final关键字的其他用法,我们很容易可以想到使用final关键字修饰类的作用,那就是用final修饰的类是无法被继承的。

17、编写多线程程序有几种实现方式?
一种是继承Thread类;另一种是实现Runnable接口。两种方式都要通过重写run() 方法来走义线程的行为,推荐使用后者,因为Java中的继承是单继承,—个类有一个父类, 假如继承了 Thread类就无法再继承其他类了,显然使用Runnable接口更为灵敏。

18、为什么要使用线程池?

(1)降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。

(2)提高响应速度。当任务到达时,任务可以不需要的等到线程创建就能立即执行。

(3)提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。

使用线程池的好处是减少在创建和销毁线程上所消耗的时间以及系统资源开销,解决资源不足的问题。如果不使用线程池,有可能会造成系统创建大量同类线程而导致消耗完内存或者“过度切换”的问题。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值