Java面试题-JavaSE基础2

一、Java 的数据类型

  1. Java 的基本数据类型都有哪些?
    布尔型---- boolean
    字符型---- char
    整数型---- byte(字节), short(短整型), int(整型), long(长整型)
    浮点型---- float(单精度浮点型), double(双精度浮点型)
  2. String 是基本数据类型吗?
    String 是引用类型,底层用 char 数组实现的。
  3. short s1 = 1; s1 = s1 + 1; 有错吗?short s1 = 1; s1 += 1 有错吗?
    前者不正确,后者正确。
    对于 short s1 = 1; s1 = s1 + 1;由于 1 是 int 类型,因此 s1+1 运算结果也是 int 型,需要强制转换类型才能赋值给 short 型。而 short s1 = 1; s1 += 1;可以正确编译,因为 s1+= 1;相当于 s1 = (short)(s1 + 1);其中有隐含的强制类型转换。
  4. int 和 和 Integer 有什么区别?
    Java 是一个近乎纯洁的面向对象编程语言,但是为了编程的方便还是引入了基本数据类型,为了能够将这些基本数据类型当成对象操作,Java 为每一个基本数据类型都引入了对应的包装类型(wrapper class),int 的包装类就是Integer,从 Java 5 开始引入了自动装箱/拆箱机制,使得二者可以相互转换。
    Java 为每个原始类型提供了包装类型:
    原始类型: boolean,char,byte,short,int,long,float,double
    包装类型:Boolean,Character,Byte,Short,Integer,Long,Float,Double
  5. String、StringBuffer、StringBuilder 的区别?
    (1)、可变不可变
    String:字符串常量,在修改时不会改变自身;若修改,等于重新生成新的字符串对象。
    StringBuffer:在修改时会改变对象自身,每次操作都是对 StringBuffer 对象本身进行修改,不是生成新的对象;使用场景:对字符串经常改变情况下,主要方法:append(),insert()等。
    (2)、线程是否安全
    String:对象定义后不可变,线程安全。
    StringBuffer:是线程安全的(对调用方法加入同步锁),执行效率较慢,适用于多线程下操作字符串缓冲区大量数据。
    StringBuilder:是线程不安全的,适用于单线程下操作字符串缓冲区大量数据。
    (3)、共同点
    StringBuilder 与 StringBuffer 有公共父AbstractStringBuilder(抽象类)。
    StringBuilder、StringBuffer 的方法都会调用 AbstractStringBuilder 中的公共方法,如 super.append(…)。只是 StringBuffer 会在方法上加 synchronized 关键字,进行同步。最后,如果程序不是多线程的,那么使用StringBuilder 效率高于 StringBuffer。
  6. 数据类型之间的转换
    (1)、字符串如何转基本数据类型?
    调用基本数据类型对应的包装类中的方法 parseXXX(String)或 valueOf(String)即可返回相应基本类型。
    (2)、基本数据类型如何转字符串?
    一种方法是将基本数据类型与空字符串(“”)连接(+)即可获得其所对应的字符串;另一种方法是调用 String 类中的 valueOf()方法返回相应字符串。

二、Java 的 IO流

  1. Java 中有几种类型的流
    按照流的方向:输入流(inputStream)和输出流(outputStream)。
    按照实现功能分:节点流(可以从或向一个特定的地方(节点)读写数据。如 FileReader)和处理流(是对一个已存在的流的连接和封装,通过所封装的流的功能调用实现数据读写。如 BufferedReader。处理流的构造方法总是要带一个其他的流对象做参数。一个流对象经过其他流的多次包装,称为流的链接。)
    按照处理数据的单位:字节流和字符流。字节流继承于 InputStream OutputStream,字符流继承于InputStreamReader 和OutputStreamWriter。
  2. 字节流如何转为字符流?
    字节输入流转字符输入流通过 InputStreamReader 实现,该类的构造函数可以传入 InputStream 对象。
    字节输出流转字符输出流通过 OutputStreamWriter 实现,该类的构造函数可以传入 OutputStream 对象。
  3. 字节流和字符流的区别
    <1>、字节流读取的时候,读到一个字节就返回一个字节; 字符流使用了字节流读到一个或多个字节(中文对应的字节数是两个,在 UTF-8 码表中是 3 个字节)时。先去查指定的编码表,将查到的字符返回。 字节流可以处理所有类型数据,如:图片,MP3,AVI 视频文件,而字符流只能处理字符数据。只要是处理纯文本数据,就要优先考虑使用字符流,除此之外都用字节流。字节流主要是操作 byte 类型数据,以 byte 数组为准,主要操作类就是 OutputStream、InputStream。
    <2>、字符流处理的单元为 2 个字节的 Unicode 字符,分别操作字符、字符数组或字符串,而字节流处理单元为 1 个字节,操作字节和字节数组。所以字符流是由 Java 虚拟机将字节转化为 2 个字节的 Unicode 字符为单位的字符而成的,所以它对多国语言支持性比较好!如果是音频文件、图片、歌曲,就用字节流好点,如果是关系到中文(文本)的,用字符流好点。在程序中一个字符等于两个字节,java 提供了 Reader、Writer 两个专门操作字符流的类。
  4. 什么是 java 序列化,如何实现 java 序列化?
    序列化就是一种用来处理对象流的机制,所谓对象流也就是将对象的内容进行流化。可以对流化后的对象进行读写操作,也可将流化后的对象传输于网络之间。序列化是为了解决在对对象流进行读写操作时所引发的问题。
    序 列 化 的 实 现 : 将 需 要 被 序 列 化 的 类 实 现 Serializable 接 口 , 该 接 口 没 有 需 要 实 现 的 方 法 ,implements Serializable 只是为了标注该对象是可被序列化的,然后使用一个输出流(如:FileOutputStream)来构造一个ObjectOutputStream(对象流)对象,接着,使用ObjectOutputStream 对象的writeObject(Object obj)方法就可以将参数为 obj 的对象写出(即保存其状态),要恢复的话则用输入流。

三、Java 的集合

  1. List 的三个子类的特点
    ArrayList 底层结构是数组,底层查询快,增删慢。
    LinkedList 底层结构是链表型的,增删快,查询慢。
    voctor 底层结构是数组 线程安全的,增删慢,查询慢。
  2. List 和 Map、Set 的区别
    2.1 结构特点
    List 和 Set 是存储单列数据的集合,Map 是存储键和值这样的双列数据的集合;List 中存储的数据是有顺序,并且允许重复;Map 中存储的数据是没有顺序的,其键是不能重复的,它的值是可以有重复的,Set 中存储的数据是无序的,且不允许有重复,但元素在集合中的位置由元素的 hashcode 决定,位置是固定的(Set 集合根据 hashcode 来进行数据的存储,所以位置是固定的,但是位置不是用户可以控制的,所以对于用户来说 set 中的元素还是无序的;
    2.2 实现类
    List 接口有三个实现类(LinkedList:基于链表实现,链表内存是散乱的,每一个元素存储本身内存地址的同时还存储下一个元素的地址。链表增删快,查找慢;ArrayList:基于数组实现,非线程安全的,效率高,便于索引,但不便于插入删除;Vector:基于数组实现,线程安全的,效率低)。
    Map 接口有三个实现类(HashMap:基于 hash 表的 Map 接口实现,非线程安全,高效,支持 null 值和 null键;HashTable:线程安全,低效,不支持 null 值和 null 键;LinkedHashMap:是 HashMap 的一个子类,保存了记录的插入顺序;SortMap 接口:TreeMap,能够把它保存的记录根据键排序,默认是键值的升序排序)。
    Set 接口有两个实现类(HashSet:底层是由 HashMap 实现,不允许集合中有重复的值,使用该方式时需要重写 equals()和 hashCode()方法;LinkedHashSet:继承与 HashSet,同时又基于 LinkedHashMap 来进行实现,底层使用的是 LinkedHashMp)。
    2.3 区别
    List 集合中对象按照索引位置排序,可以有重复对象,允许按照对象在集合中的索引位置检索对象,例如通过list.get(i)方法来获取集合中的元素;Map 中的每一个元素包含一个键和一个值,成对出现,键对象不可以重复,值对象可以重复;Set 集合中的对象不按照特定的方式排序,并且没有重复对象,但它的实现类能对集合中的对象按照特定的方式排序,例如 TreeSet 类,可以按照默认顺序,也可以通过实现 Java.util.Comparator接口来自定义排序方式。
  3. HashMap 和 HashTable 有什么区别?
    HashMap 是线程不安全的,HashMap 是一个接口,是 Map 的一个子接口,是将键映射到值得对象,不允许键值重复允许空键和空值;由于非线程安全,HashMap 的效率要较 HashTable 的效率高一些.
    HashTable 是线程安全的一个集合,不允许 null 值作为一个 key 值或者 Value 值;HashTable 是 sychronize,多个线程访问时不需要自己为它的方法实现同步,而 HashMap 在被多个线程访问的时候需要自己为它的方法实现同步。
  4. Java 中 ArrayList 和 Linkedlist 区别?
    ArrayList 和 Vector 使用了数组的实现,可以认为 ArrayList 或者 Vector 封装了对内部数组的操作,比如向数组中添加,删除,插入新的元素或者数据的扩展和重定向。
    LinkedList 使用了循环双向链表数据结构。与基于数组的 ArrayList 相比,这是两种截然不同的实现技术,这也决定了它们将适用于完全不同的工作场景。
  5. List a=new ArrayList()和 ArrayList a =new ArrayList()的区别?
    List list = new ArrayList();这句创建了一个 ArrayList 的对象后把上溯到了 List。此时它是一个 List 对象了,有些ArrayList 有但是 List 没有的属性和方法,它就不能再用了。而 ArrayList list=new ArrayList();创建一对象则保留了ArrayList 的所有属性。 所以需要用到 ArrayList 独有的方法的时候不能用前者。实例代码如下:
    1.List list = new ArrayList();
    2.ArrayList arrayList = new ArrayList();
    3.list.trimToSize(); //错误,没有该方法。
    4.arrayList.trimToSize(); //ArrayList 里有该方法。
  6. Map 中的 key 和 value 可以为 null 么?
    HashMap 对象的 key、value 值均可为 null。
    HahTable 对象的 key、value 值均不可为 null。
    且两者的的 key 值均不能重复,若添加 key 相同的键值对,后面的 value 会自动覆盖前面的 value,但不会报错。
    测试代码如下:
    1.public class Test {
    2.public static void main(String[] args) {
    3.Map<String, String> map = new HashMap<String, String>();//HashMap 对象
    4.Map<String, String> tableMap = new Hashtable<String, String>();//HashTable 对象
    5.map.put(null, null);
    6.System.out.println(“hashMap 的[key]和[value]均可以为 null:” + map.get(null));
    7.try {
    8.tableMap.put(null, “3”);
    9.System.out.println(tableMap.get(null));
    10.} catch (Exception e) {
    11.System.out.println("【ERROR】:hashTable 的[key]不能为 null");
    12.}
    13.try {
    14.tableMap.put(“3”, null);
    15.System.out.println(tableMap.get(“3”));
    16.} catch (Exception e) {
    17.System.out.println("【ERROR】:hashTable 的[value]不能为 null");
    18.}
    19.}
    20.}
    运行结果:
    hashMap 的[key]和[value]均可以为 null:null
    【ERROR】:hashTable 的[key]不能为 null
    【ERROR】:hashTable 的[value]不能为 null

四、Java 的多线程和并发库

  1. 多线程的创建方式
    (1)、继承 Thread 类:但 Thread 本质上也是实现了 Runnable 接口的一个实例,它代表一个线程的实例,并且,启动线程的唯一方法就是通过 Thread 类的 start()实例方法。start()方法是一个 native 方法,它将启动一个新线程,并执行 run()方法。这种方式实现多线程很简单,通过自己的类直接 extendThread,并复写 run()方法,就可以启动新线程并执行自己定义的 run()方法。例如:继承 Thread 类实现多线程,并在合适的地方启动线程
    1.public class MyThread extends Thread {
    2. public void run() {
    3. System.out.println(“MyThread.run()”);
    4. }
    5.}
    6.MyThread myThread1 = new MyThread();
    7.MyThread myThread2 = new MyThread();
    8.myThread1.start();
    9.myThread2.start();
    (2)、实现 Runnable 接口的方式实现多线程,并且实例化 Thread,传入自己的 Thread 实例,调用 run( )方法
    1.public class MyThread implements Runnable {
    2. public void run() {
    3. System.out.println(“MyThread.run()”);
    4. }
    5.}
    6.MyThread myThread = new MyThread();
    7.Thread thread = new Thread(myThread);
    8.thread.start();
  2. 在 java 中 wait 和 sleep 方法的不同?
    最大的不同是在等待时 wait 会释放锁,而 sleep 一直持有锁。wait 通常被用于线程间交互,sleep 通常被用于暂停执行。
  3. synchronized 和 volatile 关键字的作用
    一旦一个共享变量(类的成员变量、类的静态成员变量)被 volatile 修饰之后,那么就具备了两层语义:
    1)保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的。
    2)禁止进行指令重排序。
    volatile 本质是在告诉 jvm 当前变量在寄存器(工作内存)中的值是不确定的,需要从主存中读取;synchronized 则是锁定当前变量,只有当前线程可以访问该变量,其他线程被阻塞住。
    1.volatile 仅能使用在变量级别;synchronized 则可以使用在变量、方法、和类级别的
    2.volatile 仅能实现变量的修改可见性,并不能保证原子性;synchronized 则可以保证变量的修改可见性和原子性
    3.volatile 不会造成线程的阻塞;synchronized 可能会造成线程的阻塞。
    4.volatile 标记的变量不会被编译器优化;synchronized 标记的变量可以被编译器优化
  4. 什么是线程池,如何使用?
    线程池就是事先将多个线程对象放到一个容器中,当使用的时候就不用 new 线程而是直接去池中拿线程即可,节省了开辟子线程的时间,提高的代码执行效率。在 JDK 的 java.util.concurrent.Executors 中提供了生成多种线程池的静态方法。
    1.ExecutorService newCachedThreadPool = Executors.newCachedThreadPool();
    2.ExecutorService newFixedThreadPool = Executors.newFixedThreadPool(4);
    3.ScheduledExecutorService newScheduledThreadPool = Executors.newScheduledThreadPool(4);
    4.ExecutorService newSingleThreadExecutor = Executors.newSingleThreadExecutor();
    然后调用他们的 execute 方法即可。
  5. 常用的线程池有哪些?
    newSingleThreadExecutor:创建一个单线程的线程池,此线程池保证所有任务的执行顺序按照任务的提交顺序执行。
    newFixedThreadPool:创建固定大小的线程池,每次提交一个任务就创建一个线程,直到线程达到线程池的最大大小。
    newCachedThreadPool:创建一个可缓存的线程池,此线程池不会对线程池大小做限制,线程池大小完全依赖于操作系统(或者说 JVM)能够创建的最大线程大小。
    newScheduledThreadPool:创建一个大小无限的线程池,此线程池支持定时以及周期性执行任务的需求。
    newSingleThreadExecutor:创建一个单线程的线程池。此线程池支持定时以及周期性执行任务的需求。
  6. 请叙述一下您对线程池的理解?
    (如果问到了这样的问题,可以展开的说一下线程池如何用、线程池的好处、线程池的启动策略)合理利用线程池能够带来三个好处。
    第一:降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
    第二:提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。
    第三:提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。
  7. 什么是线程死锁?死锁产生的条件?
    7.1 死锁的定义:所谓死锁是指多个线程因竞争资源而造成的一种僵局(互相等待),若无外力作用,这些进程都将无法向前推进。
    7.2 死锁产生的必要条件:
    互斥条件:线程要求对所分配的资源(如打印机)进行排他性控制,即在一段时间内某 资源仅为一个线程所占有。此时若有其他线程请求该资源,则请求线程只能等待。
    不剥夺条件:线程所获得的资源在未使用完毕之前,不能被其他线程强行夺走,即只能由获得该资源的线程自己来释放(只能是主动释放)。
    请求和保持条件:线程已经保持了至少一个资源,但又提出了新的资源请求,而该资源已被其他线程占有,此时请求进程被阻塞,但对自己已获得的资源保持不放。
    循环等待条件:存在一种线程资源的循环等待链,链中每一个线程已获得的资源同时被链中下一个线程所请求。
  8. 线程和进程的区别
    进程:具有一定独立功能的程序关于某个数据集合上的一次运行活动,是操作系统进行资源分配和调度的一个独立单位。
    线程:是进程的一个实体,是 cpu 调度和分派的基本单位,是比进程更小的可以独立运行的基本单位。
    特点:线程的划分尺度小于进程,这使多线程程序拥有高并发性,进程在运行时各自内存单元相互独立,线程之间内存共享,这使多线程编程可以拥有更好的性能和用户体验
    注意:多线程编程对于其它程序是不友好的,占据大量 cpu 资源。
  9. 请说出同步线程及线程调度相关的方法?
    wait():使一个线程处于等待(阻塞)状态,并且释放所持有的对象的锁
    sleep():使一个正在运行的线程处于睡眠状态,是一个静态方法,调用此方法要处理 InterruptedException 异常;
    notify():唤醒一个处于等待状态的线程,当然在调用此方法的时候,并不能确切的唤醒某一个等待状态的线程,而是由 JVM 确定唤醒哪个线程,而且与优先级无关;
    notityAll():唤醒所有处于等待状态的线程,该方法并不是将对象的锁给所有线程,而是让它们竞争,只有获得锁的线程才能进入就绪状态;
    注意:java 5 通过 Lock 接口提供了显示的锁机制,Lock 接口中定义了加锁(lock()方法)和解锁(unLock()方法),增强了多线程编程的灵活性及对线程的协调。
  10. 启动一个线程是调用 run()方法还是 start()方法?
    启动一个线程是调用 start()方法,使线程所代表的虚拟处理机处于可运行状态,这意味着它可以由 JVM 调度并执行,这并不意味着线程就会立即运行。run()方法是线程启动后要进行回调(callback)的方法。

五、Java 内部类

  1. 静态嵌套类 (Static Nested Class) 和内部类(Inner Class)的不同?
    静态嵌套类:Static Nested Class 是被声明为静态(static)的内部类,它可以不依赖于外部类实例被实例化。
    内部类:需要在外部类实例化后才能实例化,其语法看起来挺诡异的。
  2. 下面的代码哪些地方会产生编译错误?
    1.class Outer {
    2.class Inner {}
    3.public static void foo() { new Inner(); }
    4.public void bar() { new Inner(); }
    5.public static void main(String[] args) {
    6.new Inner();
    7.}
    8.}
    注意:Java 中非静态内部类对象的创建要依赖其外部类对象,上面的面试题中 foo 和 main 方法都是静态方法,静态方法中没有 this,也就是说没有所谓的外部类对象,因此无法创建内部类对象,如果要在静态方法中创建内部类对象,可以这样做
    1.new Outer().new Inner();
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值