Java面试合集(基础篇)

Java面试合集(基础篇)

1.Java跨平台原理

  • 为什么要跨平台?
    由于各种操作系统支持的指令集,不是完全一致的,就会让我们的程序在不同的操作系统上要执行不同程序代码变得复杂。(操作系统指令集的差异性)
  • 怎么去实现跨平台的?
    Java开发适用于不同操作及位数的Java虚拟机来屏蔽系统之间的差异,提供统一的接口,对于我们Java开发者而言,只需要安装上对应的不同Java虚拟机,这时候你的Java程序只要遵循Java规范就可以了。

总结:Java通过不同的系统,不同的版本,不同的位数的Java虚拟机(JVM)来屏蔽不同的系统指令集差异性对外提供统一的接口(JavaAPI),对于我们普通的Java开发者而言,只需要按照接口开发即可。如果要在不同的系统上运行Java程序,只需要安装不同的JVM虚拟机。

2.搭建Java环境

  • 环境搭建需要些什么?
    • 开发环境jdk
    • idea
    • 服务器(web)

3.Java中有几种数据类型,都占几个字节

Java面试合集(基础篇)_第1张图片

4.面向对象的特征有哪几个方面

  • 封装
    将一个对象封装成一个高度自治和相对封闭的个体,对象的属性由这个对象自己的方法去操作,只提供对外的接口。(关键就是将属性私有化,然后提供公共的对外接口)

  • 抽象
    抽象就是找出一类事物相似或者共性之处,然后将这些事物归为一类,这个类只考虑这些事物的共性之处,而且会忽略与当前主题和目标无关的方向,将注意力集中在与当前目标有关的方向

  • 继承
    在定义和实现一个类的时候,可以在一个已经存在的类的基础上来进行,把这个已经存在的类所定义的内容作为自己内容 ,或者修改原来的方法(重写)使之更加适合自己的需求

  • 多态
    多态指的是程序中定义的引用变量所指向的具体类型和通过该引用发出的方法调用并不确定,而是程序运行时才确定的。即一个变量到底会指向哪个类的实例对象,该引用变量发出的方法调用到底是哪个类中实现的方法,必须由程序运行期间才能决定。

    也就是说:父类或者接口定义的引用变量可以指向子类或者具体实现类的实现对象,而程序调用方法在运行时期才动态绑定,就是引用变量指向具体实例对象的方法,也就是内存中正在运行的哪个方法,而不是引用变量的类型中定义的方法。

5.已经有了基本数据类型,为什么还需要包装类

  • 包装类型:
    每一个基本数据类型都会对应一个包装类型:
    Java面试合集(基础篇)_第2张图片

  • 装箱和拆箱

    • 装箱:把基本数据类型转换为对应的包装类型:
      自动装箱:Integer i = 1;实际上在编译时会调用一个Tnteger.ValueOf方法来装箱
    • 拆箱:把包装类型转换为对应的基本数据类型,
      Integer i = new Integer(1);
      自动拆箱:int num = i; 实际上在编译时会调用一个intValue方法来装箱
      手动拆箱:int i = i.intValue();
  • 为什么要使用包装类?

    • 我们知道Java是一个面相对象的编程语言,基本类型并不具有对象的性质,为了让基本类型也具有对象的特征,就出现了包装类型(如我们在使用集合类型Collection时就一定要使用包装类型而非基本类型),它相当于将基本类型“包装起来”,使得它具有了对象的性质,并且为其添加了属性和方法,丰富了基本类型的操作。
    • 另外,当需要往ArrayList,HashMap中放东西时,像int,double这种基本类型是放不进去的,因为容器都是装object的,这是就需要这些基本类型的包装器类了。
  • 包装类型和基本类型的区别

    • 声明方式不同
      基本类型不使用new关键字,而包装类型需要使用new关键字来在堆中分配存储空间;
    • 存储方式及位置不同
      基本类型是直接将变量值存储在栈中,而包装类型是将对象放在堆中,然后通过引用来使用;
    • 初始值不同
      基本类型的初始值如int为0,boolean为false,而包装类型的初始值为null;
    • 使用方式不不同
      基本类型直接赋值直接使用就好,而包装类型在集合如Collection、Map时会使用到。
  • Integer的缓存机制
    Integer的缓存机制问题, Integer是对小数据(-128 ~ 127)是有缓存的,再jvm初始化的时候,数据-128 ~ 127之间的数字便被缓存到了本地内存中,这样,如果初始化-128 ~ 127之间的数字,便会直接从内存中取出,而不需要再新建一个对象。但是当超过这个范围的时候我们就必须要new一个对象了

    如下案例:

        Integer x = 127;
        Integer y = 127;
        System.out.println(x==y);//true
        
        Integer m = 128;
        Integer n = 128;
        System.out.println(m==n);//false
    

    第一个打印true说明对象的引用地址时一样的,因为127在Integer的缓存中是已经存在的,所以拿出来的都是缓存中那一个对象
    第二个打印false是因为128没有在缓存中,每封装一个需要重新new一个对象,所以是两个对象

6.“==”和equals方法的区别

  • “==”
    “==”是用来判断两个变量之间的值是否相等,变量分为基本数据类型和引用数据类型。如果是基本数据类型的话直接比较其值,如果是引用数据类型的话比较的是其首地址值

  • equals
    默认的equals方法比较的是两个对象的值
    Java面试合集(基础篇)_第3张图片

7.String,StringBuilder,StringBuffer的区别

  • 1.可变/不可变
    • String是内容不可变的字符串。String底层使用了一个不可变的字符数组(final修饰的char[])
    • StringBuilder,StringBuffer是可变的,底层使用了一个可变的字符数组(非final修饰的字符数组)
  • 2.拼接
    • String拼接并不是真正的拼接,比如:
      String str = "abc"+"def";
      
      这种拼接是新建了一个引用为str的String对象
    • StringBuilder和StringBuffer拼接使用append方法进行拼接
  • 3.StringBuilder和StringBuffer的区别
    • StringBuilder线程不安全,效率高
    • StringBuffer线程安全,效率低

8.Java中的集合(Conllection)

Java中的集合分为Conllection(单链集合)和,Map(键值对)两种

  • 1.单链集合有:List 和 Set
    • List,有序,可以重复
    • Set,无需,不可以重复,根据equals和hashcode判断保证元素唯一性,也即是说如果一个对象要存储在Set中,就必须重写equals和hashcode方法
  • 2.ArrayList和LinkedList的区别
    • ArrayList底层结构是数组,LinkedList的底层是链表。
    • 数组的特点是查询快,增删改慢(数组在内存中是一块连续的内存,如果插入或者是删除是需要移动内存的,所以慢);
    • 链表的特点是增删改块,查询慢(查询时需要从头部开始一个一个找,所以效率低,插入时不需要移动内存,只需要改变引用指向即可,所以插入或者删除的效率高)

9.Java中的集合(Map)

  • 1.HashMap和HashTable的区别?

    • HashMap和HashTable都可以用来存储键值对
    • HashMap允许null值null键,HashTable不允许null值null键
    • HashMap是线程不安全的,效率高;HashTable线程安全,效率低
  • 2.HashMap和ConcurrentHashMap区别,以及ConcurrentHashMap怎么同时实现线程安全和提升效率的?

    • ConcurrentHashMap线程安全
    • ConcurrentHashMap通过将Map分为N个Segment(类似于HashTable),可以提供相同的线程安全,但是效率提升了N倍,默认提升了16倍。也就是说ConcurrentHashMap将大的Map分为一个个小的类似于HashTable的Map,那么即保证了线程安全,又可以在前一段执行的同时后一段也执行,提高了效率。

10.要实现一个拷贝文件的操作,使用字节流还是字节流

我们拷贝的文件内容不确定的时候,有可能是图片,音频,视频等文件。为了考虑到通用性,要使用字节流去实现。

11.讲一下线程的集中实现方式

  • 1. 实现方式:

    • 1)通过继承Thread类实现一个线程,重写run方法
      Java是单继承,这种实现方式如果继承了Thread类就不能继承别的类的,扩展性不强。
    • 2)通过实现一个Runnable接口,重写run方法
      没有返回值,使用了静态代理的模式。
    • 3)通过实现一个Callable接口,重写call方法
      有返回值,
  • 2.怎么启动?

    • 调用线程类的start方法;
    • 启动线程使用start方法,而启动了之后执行的是run方法
  • 3.怎么区分线程?在一个系统中有很多线程,每个线程都都会打印日志,我想区分是哪个线程打印的怎么办

    • 给线程设置一个名字:thead.setName("线程1’);

12.有没有使用过线程并发库?了解过线程池吗?

在JDK1.5中增加了并发库,在java.util.current包中提供了对线程优化、管理的各项操作,使得线程的使用变得更加方便。该包提供了线程的运行,线程池的创建,线程生命周期的控制。

Java中怎么创建线程池?

Java通过Executors提供的四个静态方法创建线程池:

  • newCachedThreadPool():创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,如果没有可回收的线程,就创建新线程。
  • newFixedThreadPool():创建一个固定长度的线程池,可控制线程最大并发数,超出的线程会在队列中等待
  • newScheduledThreadPool():创建一个定长的线程池,支持定时以及周期性任务执行
  • newSingleThreadExecutor():创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO,UFO)执行
线程池的作用?
  • 1.限定线程的个数,不会导致线程过多导致系统运行缓慢或者崩溃
  • 2.线程池不需要每次都去创建,节约了资源
  • 3.线程池不需要每次都去创建,响应时间更快

13.什么是设计模式?常用的设计模式有哪些?

设计模式是什么?

设计模式就是经过前人经过无数次实践总结出来的,设计过程中可以反复使用,可以解决特定问题的设计方法。

常见的设计有哪些?
  • 单例模式(饱汉模式/恶汉模式):
    1)构造方法私有化,让除了自己类中能创建外其他地方不能创建
    2)让自己的类中创建一个单实例(饿汉式一出来就直接创建实例,饱汉式等到需要的时候再去创建)
    3)提供一个public方法获取实例对象
    • 饿汉式:

      public class SingletonMode {
          //构造私有化
          private SingletonMode(){ }
          //2.创建实例
          private static final SingletonMode singletonMode = new SingletonMode();
          //3.提供唯一获取实例的静态方法
          public static SingletonMode getSingletonMode(){
              return singletonMode;
          }
      }
      
    • 懒汉式:

      public class SingletonMode {
          //构造私有化
          private SingletonMode(){ }
          //2.先不创建对象,等用到的时候再创建
          private static SingletonMode singletonMode = null;
          //3.提供唯一获取实例的方法
          public static SingletonMode getSingletonMode(){
              //4.判断对象是否为空,为空就创建
              if (singletonMode==null){
                  singletonMode=new SingletonMode();
              }
              return singletonMode;
          }
      }
      
    • 懒汉式(方法上加锁):

      public class SingletonMode {
          //构造私有化
          private SingletonMode(){ }
          //2.先不创建对象,等用到的时候再创建
          private static SingletonMode singletonMode = null;
          //3.提供唯一获取实例的方法
          public static synchronized SingletonMode getSingletonMode(){
              //4.判断对象是否为空,为空就创建
              if (singletonMode==null){
                  singletonMode=new SingletonMode();
              }
              return singletonMode;
          }
      }
      
    • 懒汉式(双重检测机制(DCL)懒汉式,进阶懒汉式):

      public class SingletonMode {
          private SingletonMode() {}
          // 可能会有重排序问题,加上 volatile 关键字解决
          // volatile 有内存屏障的功能
          private static volatile SingletonMode singletonMode = null;
          public static SingletonMode getSingletonMode() {
              // 这里判断是否为空,主要是为了提高性能
              if (singletonMode == null) {
      
                  // 锁住这里就行了,里面还有一个判断是否为空
                  // 将锁的范围缩小,提高性能
                  synchronized (SingletonMode.class) {
      
                      // 再判断一次是否为null
                      // 如果只在外面加 null 判断,若两个线程同时调用 getSingletonMode,同时判断外面的 null,那也会创建出两个对象
                      // 所以在锁的里面也要加上 null 判断
                      if (singletonMode == null) {
                          singletonMode = new SingletonMode();
                      }
                  }
              }
              return singletonMode;
          }
      }
      
    • 懒汉式(静态内部类写法,推荐):
      原理:当任何一个线程第一次调用 getInstance()时,都会使 SingletonHolder 被加载和被初始化,此时静态初始化器将执行Singleton的初始化操作。(被调用时才进行初始化)。初始化静态数据时,Java 提供了的线程安全性保证(所以不需要任何的同步)

      public class SingletonMode {
          private SingletonMode() {}
      
          // 使用内部类的方式来实现懒加载
          private static class LazyHolder {
              // 创建单例对象
              private static final SingletonMode INSTANCE = new SingletonMode();
          }
      
          // 获取对象
          public static final SingletonMode getInstance() {
              return LazyHolder.INSTANCE;
          }
      }
      
    • 懒汉式(枚举写法,最安全、简洁写法,推荐写法):

      public enum  SingletonMode{
          INSTANCE;
      
          private Object object;
          // 创建单例对象
          SingletonMode() {
              object = new Object();
          }
          // 获取单例对象
          public Object getInstance() {
              return object;
          }
      }
      

      枚举的方式实现:如上代码,外部只能通过 SingletonMode的 getInstance 来获得 object对象,再没有其他途径
      因为枚举变量是 static final 的,所以如果不是定义时声明,那只能在构造方法中实例化,并且有且只能实例化一次

      所以保证了 object对象的单例性

      当我们使用 enum 关键字定义一个枚举的时候,他会帮我们在编译后默认继承 java.lang.Enum 类,而不像其他的类一样默认继承 Object 类。且采用 enum 声明后,该类会被编译器加上 final 声明,故该类是无法继承的

      由于JVM类初始化是线程安全的,所以可以采用枚举类实现一个线程安全的单例模式

      简单写法
      防止多次实例化,即使是在面对复杂的序列化或者反射攻击的时候(安全)

  • 工厂模式
    对象的创建交给一个工厂去创建,Spring IOC就是使用了工厂模式
  • 代理模式
    创建线程时使用到的实现Runnable接口使用了静态代理模式,Sping中的AOP使用了动态代理模式

你可能感兴趣的:(面试合集)