Java常见面试题整理(Java基础)

1、JDK和JRE有什么区别?

JDK就是Java Development Kit 。JDK是面向开发人员使用的SDK,它提供了Java的开发环境和运行环境。SDK是Software Development Kit 一般指软件开发包,可以包括函数库、编译程序等。

JRE是Java Runtime Enviroment是指Java的运行环境,是面向Java程序的使用者,而不是开发者。

2、== 和 equals 的区别是什么?

(1)== : 它的作用是判断两个对象的地址是不是相等。即,判断两个对象是不是同一个对象。
(基本数据类型 == 比较的是值,引用数据类型 == 比较的是内存地址)

(2)equals() : 它的作用也是判断两个对象是否相等。
但它一般有两种使用情况:
情况1:类没有覆盖 equals() 方法。则通过 equals() 比较该类的两个对象时,等价于通过“==”比较这两个对象。

情况2:类覆盖了 equals() 方法。一般,我们都覆盖 equals() 方法来两个对象的内容相等;若它们的内容相等,则返回 true (即,认为这两个对象相等)。

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

如果两个对象相等,则hashcode一定也是相同的

两个对象相等,对两个对象分别调用equals方法都返回true

两个对象有相同的hashcode值,它们也不一定是相等的

因此,equals 方法被覆盖过,则 hashCode 方法也必须被覆盖

hashCode() 的默认行为是对堆上的对象产生独特值。如果没有重写 hashCode(),则该 class 的两个对象无论如何都不会相等(即使这两个对象指向相同的数据)

4、final 在 java 中有什么作用?

用于修饰类、属性和方法;

被final修饰的类不可以被继承
被final修饰的方法不可以被重写
被final修饰的变量不可以被改变,被final修饰不可变的是变量的引用,而不是引用指向的内容,引用指向的内容是可以改变的

5、java 中的 Math.round(1.5) 等于多少? Math.round(-1.5) 等于多少?

Math.round(1.5)等于2,Math.round(-1.5)等于-1,四舍五入是在原来的基础上加上0.5向下取整。

6、说出以下代码的输出结果。

 public class A {
   static{
      System.out.println("static in class A");
   }

   public A(){
      System.out.println("class A");
   }
}
 public class SubA extends A {
   static {
      System.out.println("static in class SubA");
   }

   public SubA() {
      super();
      System.out.println("class SubA");
   }

   public SubA(String sa) {
      System.out.println("class SubA " + sa);
   }
}
 public class StaticTest {
   public static void main(String[] args) {
      SubA subA1 = new SubA("111111");
      SubA subA2 = new SubA("222222");
      SubA subA3 = new SubA("333333");
   }
}
输出结果为:
 static in class A
static in class SubA
class A
class SubA 111111
class A
class SubA 222222
class A
class SubA 333333

解释:
子类构造方法调用规则:
(1)如果子类的构造方法中没有通过 super 显式调用父类的有参构造方法,
也没有通过 this 显式调用自身的其他构造方法,则系统会默认先调用父类的无参构造方法。
这种情况下,写不写 super(); 语句,效果是一样的
(2)如果子类的构造方法中通过 super 显式调用父类的有参构造方法,
将执行父类相应的构造方法,不执行父类无参构造方法
(3)如果子类的构造方法中通过 this 显式调用自身的其他构造方法,将执行类中相应的构造方法
(4)如果存在多级继承关系,在创建一个子类对象时,以上规则会多次向更高一级父类应用,
一直到执行顶级父类 Object 类的无参构造方法为止
结论:
类的实例化方法调用顺序:
类加载器实例化时进行的操作步骤:加载 -> 连接 -> 初始化
(1)父类静态代变量
(2)父类静态代码块
(3)子类静态变量
(4)子类静态代码块
(5)父类非静态变量(父类实例成员变量)
(6)父类构造函数
(7)子类非静态变量(子类实例成员变量)
(8)子类构造函数

7、Java中,基本的数据类型有哪些?

基本数据类型有byte、short、int、long、float、double、char、boolean八种基本数据类型。
byte:1个字节,8位
short:2个字节,16位
int:4个字节,32位
long:8个字节,64位
float:4个字节,32位
double:8个字节,64位
boolean:逻辑上理解是占用 1位,但是实际中会考虑计算机高效存储因素
char:2个字节,16位

8、String 属于基础的数据类型吗?

不属于,Java中基本数据类型只有byte、short、int、long、float、double、char、boolean八种数据类型。
String在Java中属于对象,底层实现是char数组,使用final修饰。

9、switch 是否能作用在 byte 上,是否能作用在 long 上,是否能作用在 String 上?

在Java5以前,switch(expr)中的expr只能是byte、char、short、int四种数据类型。从Java5以后引入了枚举,expr可以是enum类型,Java7开始,expr就可以支持String字符串了,但是目前还不能使用long类型的。

10、java 中操作字符串都有哪些类?它们之间有什么区别?

Java中操作字符串的类有String、StringBuffer、StringBuilder三个类。
三者的相同点:
(1)都可以存储和操作字符串;
(2)底层都使用了final修饰,不能被继承。
(3)提供了API相似。
三者的区别:
(1)String是不可变字符序列,String内容是不能被改变的;
(2)StringBuffer和StringBuilder是可变字符序列,他们都可以对字符串内容进行修改,
并且修改之后的内存地址不会发生改变;
(3)StringBuilder是JDK1.5的,效率高,但是它的线程不安全,
StringBuffer是JDK1.0的,效率低,但是它是线程安全的(方法加了Synchronized)。

对于三者使用的总结

如果要操作少量的数据用 = String

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

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

11、String str="i"与 String str=new String(“i”)一样吗?

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

提问:String s = new String(“abc”)创建了几个对象呢?
答案是两个对象,一个是静态区的"abc",另外一个是用new创建在堆上的对象。

  String str1 = "hello"; //str1指向静态区
  String str2 = new String("hello");  //str2指向堆上的对象
  String str3 = "hello";
  String str4 = new String("hello");
  System.out.println(str1.equals(str2)); //true
  System.out.println(str2.equals(str4)); //true
  System.out.println(str1 == str3); //true
  System.out.println(str1 == str2); //false
  System.out.println(str2 == str4); //false
  System.out.println(str2 == "hello"); //false
  str2 = str1;
  System.out.println(str2 == "hello"); //true

13、如何将字符串反转?

使用StringBuffer或者StringBuilder中的reverse()方法。

// StringBuffer reverse
 StringBuffer stringBuffer = new StringBuffer();
 stringBuffer. append("abcdefg");
 System. out. println(stringBuffer. reverse()); // gfedcba
// StringBuilder reverse
 StringBuilder stringBuilder = new StringBuilder();
 stringBuilder. append("abcdefg");
 System. out. println(stringBuilder. reverse()); // gfedcba

14、String 类的常用方法都有那些?

(1)String 类的常用方法都有那些?
(2)indexOf():返回指定字符的索引。
(3)charAt():返回指定索引处的字符。
(4)replace():字符串替换。
(5)trim():去除字符串两端空白。
(6)split():分割字符串,返回一个分割后的字符串数组。
(7)getBytes():返回字符串的 byte 类型数组。
(8)length():返回字符串长度。
(9)toLowerCase():将字符串转成小写字母。
(10)toUpperCase():将字符串转成大写字符。
(11)substring():截取字符串。
(12)equals():字符串比较。

15、HashMap中使用String做key有什么好处?

HashMap内部实现是通过key的hashcode来确定value的存储位置,因为字符串是不可变的,并且使用字符串时会在字符串常量池中进行缓存,所以当创建字符串的时候,hashcode会被缓存下来,不需要再次计算,所以相比于其他对象来说快一些。

16、抽象类必须要有抽象方法吗?

抽象类不一定要有抽象方法,但是抽象方法的类必须是抽象类。

17、普通类和抽象类有哪些区别?

(1)抽象类不能被实例化;
(2)抽象类可以有抽象方法,但是普通类没有;
(3)含有抽象方法的类必须是抽象类;
(4)抽象方法不能被声明为静态;
(5)抽象方法不能被private;
(6)抽象方法不能被final修饰。

18、抽象类能使用 final 修饰吗?

不能,因为被final修饰的类不能被继承,而定义抽象类就是为了让其他类去继承的,如果使用final修饰了就矛盾了,因此不能被final修饰。

19、接口和抽象类有什么区别?

(1)实现:抽象类的子类使用extends来继承,而接口就必须使用implements来实现接口;
(2)构造函数:抽象类可以有构造函数,但是接口没有;
(3)实现数量:类可以实现很多个接口,但是只能继承一个抽象类;
(4)访问修饰符:接口的方法默认使用的是public修饰,而抽象类中的方法可以是任意访问修饰符;

20、内部类有哪几种?详细说明一下。

内部类的定义:可以将一个类的定义放在另外一个类的定义内部;
内部类的种类:成员内部类、局部内部类、匿名内部类、静态内部类。

(1)静态内部类:定义在类内部的静态类。

public class Outer {

   private static int radius = 1;

   static class StaticInner {
      public void visit() {
         System.out.println("visit outer static  variable:" + radius);
      }
   }
}

静态内部类可以访问外部类所有的静态变量,而不可访问外部类的非静态变量;静态内部类的创建方式,new 外部类.静态内部类(),如下:

     Outer.StaticInner inner = new Outer.StaticInner();
    inner.visit();

(2)成员内部类:定义在类内部,成员位置上的非静态类。

 public class Outer {

   private static  int radius = 1;
   private int count =2;

   class Inner {
      public void visit() {
         System.out.println("visit outer static  variable:" + radius);
         System.out.println("visit outer   variable:" + count);
      }
   }
}

成员内部类可以访问外部类所有的变量和方法,包括静态和非静态,私有和公有。成员内部类依赖于外部类的实例,它的创建方式外部类实例.new 内部类(),如下:

 Outer outer = new Outer();
Outer.Inner inner = outer.new Inner();
inner.visit();

(3)局部内部类:定义在方法的内部,就是局部内部类。

 public class Outer {

   private  int out_a = 1;
   private static int STATIC_b = 2;

   public void testFunctionClass(){
      int inner_c =3;
      class Inner {
         private void fun(){
            System.out.println(out_a);
            System.out.println(STATIC_b);
            System.out.println(inner_c);
         }
      }
      Inner  inner = new Inner();
      inner.fun();
   }
   public static void testStaticFunctionClass(){
      int d =3;
      class Inner {
         private void fun(){
            // System.out.println(out_a); 编译错误,定义在静态方法中的局部类不可以访问外部类的实例变量
            System.out.println(STATIC_b);
            System.out.println(d);
         }
      }
      Inner  inner = new Inner();
      inner.fun();
   }
}

定义在实例方法中的局部类可以访问外部类的所有变量和方法,定义在静态方法中的局部类只能访问外部类的静态变量和方法。局部内部类的创建方式,在对应方法内,new 内部类(),如下:

 public static void testStaticFunctionClass(){
   class Inner {
   }
   Inner  inner = new Inner();
}

(4)匿名内部类:没有名字的内部类,日常开发中使用的比较多。

 public class Outer {

   private void test(final int i) {
      new Service() {
         public void method() {
            for (int j = 0; j < i; j++) {
               System.out.println("匿名内部类" );
            }
         }
      }.method();
   }
}
//匿名内部类必须继承或实现一个已有的接口 
interface Service{
   void method();
}

除了没有名字,匿名内部类还有以下特点:
A、匿名内部类必须继承一个抽象类或者实现一个接口。
B、匿名内部类不能定义任何静态成员和静态方法。
C、当所在的方法的形参需要被匿名内部类使用时,必须声明为 final。
D、匿名内部类不能是抽象的,它必须要实现继承的类或者实现的接口的所有抽象方法。

匿名内部类创建方式:
 new 类/接口{
      //匿名内部类实现部分
      }

21、内部类有哪些使用场景?你在哪些地方使用到了内部类?

(1)在设计模式中使用静态内部类实现单例模式;

public class SingletonDemo03 {

    private static class SingletonClassInstance{
        private static final SingletonDemo03 instance = new SingletonDemo03();
    }

    public static SingletonDemo03 getInstance(){
        return SingletonClassInstance.instance;
    }

    private SingletonDemo03(){}

}

(2)见以下代码,这种方式用到了匿名内部类。

Thread thread = new Thread(new Runnable() {
  @Override
  public void run() {

  });
thread.start();

其他的忘记了,,,等记得来的时候再补上0.0

22、Files的常用方法都有哪些?

(1)Files. exists():检测文件路径是否存在。
(2)Files. createFile():创建文件。
(3)Files. createDirectory():创建文件夹。
(4)Files. delete():删除一个文件或目录。
(5)Files. copy():复制文件。
(6)Files. move():移动文件。
(7)Files. size():查看文件个数。
(8)Files. read():读取文件。
(9)Files. write():写入文件。

23、Date类常用方法都有哪些?

-----日期类型如何格式化?
      ***************************************************************************************
      DateFormat  sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
      String s = sdf.format(new Date());//日期转字符串
      ***************************************************************************************
      -----字符串如何转日期?
      ***************************************************************************************
      DateFormat  sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
      String s = "2019-10-31 22:53:10";
      Date date = sdf.parse(s);
      ***************************************************************************************
      -----如何取得当前年、月、日、时、分、秒、毫秒?
      年:Calendar.getInstance().get(Calendar.YEAR);
      或者Year.now();//JDK 1.8 java.time 包
      还有LocalDate.now().getYear();//JDK 1.8 java.time 包

      月:Calendar.getInstance().get(Calendar.MONTH)+1;
      MonthDay.now().getMonthValue();//JDK 1.8 java.time 包
      LocalDate.now().getMonthValue();//JDK 1.8 java.time 包

      日:Calendar.getInstance().get(Calendar.DAY_OF_MONTH);
      MonthDay.now().getDayOfMonth();//JDK 1.8 java.time 包
      LocalDate.now().getDayOfMonth();//JDK 1.8 java.time 包

      时:Calendar.getInstance().get(Calendar.HOUR_OF_DAY);
      LocalTime.now().getHour();//JDK 1.8 java.time 包

      分:Calendar.getInstance().get(Calendar.MINUTE);
      LocalTime.now().getMinute();//JDK 1.8 java.time 包

      秒:Calendar.getInstance().get(Calendar.SECOND);
      LocalTime.now().getSecond();//JDK 1.8 java.time 包

      毫秒:Calendar.getInstance().get(Calendar.MILLISECOND);
      ***************************************************************************************
--如何取得从1970年1月1日0时0分0秒到现在的毫秒数?
      System.currentTimeMillis();
      Calendar.getInstance().getTimeInMillis();
--如何格式化日期?
      new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
      //JDK 1.8 java.time 包
      LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));

24、java 中 IO 流分为几种?

(1)按照流向可以分为输入流和输出流;
(2)按照操作单元划分可以分为字节流和字符流;
(3)按照流的角色划分可分为节点流和处理流;

25、BIO、NIO、AIO 有什么区别?

简答:

BIO:Block IO 同步阻塞式 IO,就是我们平常使用的传统 IO,它的特点是模式简单使用方便,并发处理能力低。
NIO:Non IO 同步非阻塞 IO,是传统 IO 的升级,客户端和服务器端通过 Channel(通道)通讯,实现了多路复用。
AIO:Asynchronous IO 是 NIO 的升级,也叫 NIO2,实现了异步非堵塞 IO ,异步 IO 的操作基于事件和回调机制。

详细回答:

BIO (Blocking I/O): 同步阻塞I/O模式,数据的读取写入必须阻塞在一个线程内等待其完成。在活动连接数不是特别高(小于单机1000)的情况下,这种模型是比较不错的,可以让每一个连接专注于自己的 I/O 并且编程模型简单,也不用过多考虑系统的过载、限流等问题。线程池本身就是一个天然的漏斗,可以缓冲一些系统处理不了的连接或请求。但是,当面对十万甚至百万级连接的时候,传统的 BIO 模型是无能为力的。因此,我们需要一种更高效的 I/O 处理模型来应对更高的并发量。

NIO (New I/O): NIO是一种同步非阻塞的I/O模型,在Java 1.4 中引入了NIO框架,对应 java.nio 包,提供了 Channel , Selector,Buffer等抽象。NIO中的N可以理解为Non-blocking,不单纯是New。它支持面向缓冲的,基于通道的I/O操作方法。 NIO提供了与传统BIO模型中的 Socket 和 ServerSocket 相对应的 SocketChannel 和 ServerSocketChannel 两种不同的套接字通道实现,两种通道都支持阻塞和非阻塞两种模式。阻塞模式使用就像传统中的支持一样,比较简单,但是性能和可靠性都不好;非阻塞模式正好与之相反。对于低负载、低并发的应用程序,可以使用同步阻塞I/O来提升开发速率和更好的维护性;对于高负载、高并发的(网络)应用,应使用 NIO 的非阻塞模式来开发。

AIO (Asynchronous I/O): AIO 也就是 NIO 2。在 Java 7 中引入了 NIO 的改进版 NIO 2,它是异步非阻塞的IO模型。异步 IO 是基于事件和回调机制实现的,也就是应用操作之后会直接返回,不会堵塞在那里,当后台处理完成,操作系统会通知相应的线程进行后续的操作。AIO 是异步IO的缩写,虽然 NIO 在网络操作中,提供了非阻塞的方法,但是 NIO 的 IO 行为还是同步的。对于 NIO 来说,我们的业务线程是在 IO 操作准备好时,得到通知,接着就由这个线程自行进行 IO 操作,IO操作本身是同步的。

以上是我之前面试的时候的一些问题+基础总结,答案参考了很多大佬的技术博客,说来惭愧,当时基础不太好,问到的好多都不太会,0.0

猜你喜欢

转载自blog.csdn.net/weixin_43246215/article/details/107473181