【面试题】Java基础篇-常见面试题总结p2

备战实习,会定期的总结常考的面试题,大家一起加油! 🎯

本文章参考:

注意:如果本文中有错误的地方,欢迎评论区指正!🍭

往期链接:

🧭【面试题】计算机网络篇-10道常见面试题p1

【面试题】JVM篇-10道常见面试题p1

🎈【面试题】Java并发篇-10道常见面试题p1

😀【面试题】Java基础篇-常见面试题总结p1

1.一个类的构造方法的作用是什么?

一个类的构造方法主要作用是完成对该类对象的初始化工作。

👨‍💻面试官追问:若一个类没有声明构造方法,该程序能正确执行吗? 为什么?

  • 是可以正确执行的
  • 因为一个类即使没有声明构造方法也会有默认的不带参数的构造方法。但是如果我们自己添加了类的构造方法(无论是否有参),Java 就不会再添加默认的无参数的构造方法了,这时候,就不能直接 new 一个对象而不传递参数了。因此如果我们重载了有参的构造方法,记得都要把无参的构造方法也写出来

👨‍💻面试官继续问:构造方法有哪些特点?

  • 名字与类名相同
  • 没有返回值,但不能用 void 声明构造函数
  • 生成类的对象时自动执行,无需调用
  • 构造方法不能被 override(重写),但是可以 overload(重载)

2.成员变量与局部变量的区别有哪些?

局部变量:类的方法中的变量。

成员变量:成员变量又称全局变量,可分为类变量实例变量,有static修饰为类变量,没有static修饰为实例变量。

  1. 从语法形式上看成员变量是属于类的,而局部变量是在代码块或方法中定义的变量或是方法的参数;成员变量可以被 public,private,static 等修饰符所修饰,而局部变量不能被访问控制修饰符及 static 所修饰;但是,成员变量和局部变量都能被 final 所修饰。
  2. 从变量在内存中的存储方式来看,如果成员变量是使用 static 修饰的,那么这个成员变量是属于类的,如果没有使用 static 修饰,这个成员变量是属于实例的。成员变量存在于堆内存,局部变量则存在于栈内存。
  3. 从变量在内存中的生存时间上看,成员变量是对象的一部分,它随着对象的创建而存在,而局部变量随着方法的调用而自动消失
  4. 从变量是否有默认值来看,成员变量如果没有被赋初值,则会自动以类型的默认值而赋值(一种情况例外:被 final 修饰的成员变量也必须显式地赋值),而局部变量则不会自动赋值

👨‍💻面试官追问:那你在聊聊静态变量和实例变量的区别?

两个都是成员变量,区别是:

  • 调用方式

    在外部调用静态方法时,可以使用 类名.方法名 的方式,也可以使用 对象.方法名 的方式,而实例方法只有后面这种方式。也就是说,调用静态方法可以无需创建对象

  • 访问类成员是否存在限制

    静态方法在访问本类的成员时,只允许访问静态成员(即静态成员变量和静态方法),不允许访问实例成员(即实例成员变量和实例方法),而实例方法不存在这个限制。

👨‍💻面试官继续问:静态方法为什么不能调用非静态成员?

  1. 静态方法是属于类的,在类加载的时候就会分配内存,可以通过类名直接访问。而非静态成员属于实例对象,只有在对象实例化之后才存在,需要通过类的实例对象去访问
  2. 在类的非静态成员不存在的时候静态成员就已经存在了,此时调用在内存中还不存在的非静态成员,属于非法操作

3.具体说一下抽象类和接口有什么区别?

  1. 抽象类中可以定义构造函数,接口不能定义构造函数
  2. 抽象类中可以有抽象方法和具体方法,JDK1.8之前而接口中只能有抽象方法。但是在JDK1.8中,允许在接口中包含带有具体实现的方法,使用default修饰,这类方法就是默认方法
  3. 抽象类中的成员权限可以是publicdefaultprotected(抽象类中抽象方法就是为了重写,所以不能被private修饰),而接口中的成员只可以是public(方法默认: public abstrat、成员变量默认: public static final)
  4. 抽象类中可以包含静态方法,而接口在JDK1.8之前不能包含静态方法,JDK1.8以后可以包含,但是仍然不可以包含静态代码块
  5. 抽象类可以继承一个类和实现多个接口,类只可以继承一个抽象类。接口只可以继承接口(一个或多个),子类可以实现多个接口

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

  • 形式 :字符常量是单引号引起的一个字符,字符串常量是双引号引起的 0 个或若干个字符
  • 含义 :字符常量相当于一个整型值( ASCII 值),可以参加表达式运算; 字符串常量代表一个地址值(该字符串在内存中存放位置)
  • 占内存大小 : 字符常量只占 2 个字节; 字符串常量占若干个字节(注意: char 在 Java 中占两个字节

5.Object 的常用方法有哪些?

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jgaIW08y-1638491669800)(【面试题】Java基础篇-常见面试题总结p2.assets/image-20211129084331679.png)]

Object 类是一个特殊的类,是所有类的父类。它主要提供了以下 11 个方法:

public final native Class<?> getClass()//native方法,用于返回当前运行时对象的Class对象,使用了final关键字修饰,故不允许子类重写。

public native int hashCode() //native方法,用于返回对象的哈希码,主要使用在哈希表中,比如JDK中的HashMap。
    
public boolean equals(Object obj)//用于比较2个对象的内存地址是否相等,String类对该方法进行了重写用户比较字符串的值是否相等。

protected native Object clone() throws CloneNotSupportedException//naitive方法,用于创建并返回当前对象的一份拷贝。一般情况下,对于任何对象 x,表达式 x.clone() != x 为true,x.clone().getClass() == x.getClass() 为true。Object本身没有实现Cloneable接口,所以不重写clone方法并且进行调用的话会发生CloneNotSupportedException异常。

public String toString()//返回类的名字@实例的哈希码的16进制的字符串。建议Object所有的子类都重写这个方法。

public final native void notify()//native方法,并且不能重写。唤醒一个在此对象监视器上等待的线程(监视器相当于就是锁的概念)。如果有多个线程在等待只会任意唤醒一个。

public final native void notifyAll()//native方法,并且不能重写。跟notify一样,唯一的区别就是会唤醒在此对象监视器上等待的所有线程,而不是一个线程。

public final native void wait(long timeout) throws InterruptedException//native方法,并且不能重写。暂停线程的执行。注意:sleep方法没有释放锁,而wait方法释放了锁 。timeout是等待时间。

public final void wait(long timeout, int nanos) throws InterruptedException//多了nanos参数,这个参数表示额外时间(以毫微秒为单位,范围是 0-999999)。 所以超时的时间还需要加上nanos毫秒。

public final void wait() throws InterruptedException//跟之前的2个wait方法一样,只不过该方法一直等待,没有超时时间这个概念

protected void finalize() throws Throwable { }//实例被垃圾回收器回收的时候触发的操作

6.聊聊final、finally、finalize 的区别?

  • final:用于声明属性、方法和类,分别表示属性不可变、方法不可覆盖、被其修饰的类不可继承
  • finally:是在异常处理时提供finally块来执行任何清除操作。不管有没有异常被抛出、捕获,finally块都会被执行
  • finallize:是Object类中的一个方法,在垃圾收集器执行的时候会调用被回收对象的finalize方法,可以覆盖此方法来实现对其他资源的回收,例如关闭文件等。需要注意的是,一旦垃圾回收器准备好释放对象占用的空间,将首先调用其finalize方法,并且在下一次垃圾回收动作发生时,才会真正回收对象占用的内存

7.说说 & 和 && 的区别?

Java中&&和&都是表示与的逻辑运算符,都表示逻辑运输符and,当两边的表达式都为true的时候,整个运算结果才为true,否则为false。

  • &&有短路功能,当第一个表达式的值为false 的时候,则不再计算第二个表达式
  • &∶不管第一个表达式结果是否为true,第二个都会执行。除此之外,&还可以用作位运算符︰当&两边的表达式不是Boolean类型的时候,&表示按位操作

8.String StringBuffer 和 StringBuilder 的区别是什么?

  • String:用于字符串操作,属于不可变类
  • StringBuilder:与 StringBuffer类似,都是字符串缓冲区,但线程不安全
  • StringBuffer:也用于字符串操作,不同之处是StringBuffer属于可变类,对方法加了同步锁,线程安全

👨‍💻面试官又问:String 为什么是不可变的?

public final class String implements java.io.Serializable, Comparable<String>, CharSequence {
       private final char value[];
       //...
}
  • 保存字符串的数组被 final 修饰且为私有的,并且String 类没有提供/暴露修改这个字符串的方法。
  • String 类被 final 修饰导致其不能被继承,进而避免了子类破坏 String 不可变。

👨‍💻面试官又问:那你说说这三者适合使用的场景?

  • 操作少量的数据:适用 String
  • 单线程操作字符串缓冲区下操作大量数据:适用 StringBuilder
  • 多线程操作字符串缓冲区下操作大量数据:适用 StringBuffer

9.说一下 String str = “xpp” 和 String str = new String(“xpp”) 区别?

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

  • 在执行String str = "xpp"的时候
    1. JVM会首先检查字符串常量池中是否已经存在该字符串对象,如果已经存在,那么就不会再创建了,直接返回该字符串在字符串常量池中的内存地址
    2. 如果该字符串还不存在字符串常量池中,那么就会在字符串常量池中创建该字符串对象,然后再返回
  • 而在执行String st = new String(“xpp”)的时候
    1. JVM会首先检查字符串常量池中是否已经存在"xpp"字符串,如果已经存在,则不会在字符串常量池中再创建了;
    2. 如果不存在,则就会在字符串常量池中创建"xpp"字符串对象,然后再到堆内存中再创建一份字符串对象,把字符串常量池中的"xpp"字符串内容拷贝到内存中的字符串对象中,然后返回堆内存中该字符串的内存地址,即栈内存中存储的地址是堆内存中对象的内存地址。

10.Java中的128陷阱了解吗?

举个栗子:

/**
 * @author xppll
 * @date 2021/12/2 08:54
 */
public class test {
    public static void main(String[] args) {
        Integer   a=127,b=127;
        Integer c=128,d=128;
        System.out.println(a==b);//true
        System.out.println(c==d);//false
    }
}

我们都知道Integer 是 基本类型int 的包装类型。在Java设计之初,设计者认为,开发者可能经常用到的数字范围都在 100 以内,而每次使用这些数字的包装类型都要开辟新空间的话,可能会占用大量的资源。因此他们规定在-128~127之间的Integer类型的变量,直接指向常量池中的缓存地址,不再使用new去开辟出新的空间

执行 Integer c = 128,相当于执行:Integer c = Integer.valueOf(128),基本类型自动转换为包装类的过程称为自动装箱。
看看valueOf() 源码:

public static Integer valueOf(int i) {
    if (i >= IntegerCache.low && i <= IntegerCache.high)
        return IntegerCache.cache[i + (-IntegerCache.low)];
    return new Integer(i);
}

可以看出在Integer 中引入了IntegerCache 来缓存一定范围的值,默认情况下范围为:-128~127
因此上述代码中的 127 命中了 IntegerCache,所以 a 和 b 是相同对象,而 128 则没有命中,所以 c 和 d 是不同对象

附:基本类型对应的缓冲池如下

boolean values true and false //布尔类型中的两个取值 true和false
all byte values //byte类型所有数据,即-128~127
short values between -128 and 127 //short类型大小范围-128~127
int values between -128 and 127 //int类型大小范围 -128~127
char in the range \u0000 to \u007F //char类型所有数据,即所有字符

在这里插入图片描述
最后喜欢的小伙伴,记得三连哦!😏🍭😘

  • 28
    点赞
  • 38
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 45
    评论
java面试题真的很多,下面我来回答一个有关多线程的问题。 在Java中实现多线程有两种方式,一种是继承Thread,另一种是实现Runnable接口。这两种方式有何区别? 继承Thread的方式是直接定义一个继承Thread,并重写它的run()方法。然后创建该的对象,并调用对象的start()方法来启动线程。这种方式简单直接,但因为Java是单继承的,所以如果某个已经继承了其他,就不能再直接继承Thread实现多线程。 实现Runnable接口的方式是定义一个实现Runnable接口,并实现其唯一的抽象方法run()。然后创建Thread的对象,将实现了Runnable的对象作为参数传递给Thread构造方法。最后调用Thread对象的start()方法来启动线程。这种方式灵活性更大,因为Java允许一个实现多个接口,所以即使某个已经继承了其他,仍然可以通过实现Runnable接口来实现多线程。 另一个区别在于资源共享的问题。继承Thread的方式,不管是数据还是方法,都是线程自己拥有的,不存在共享的情况。而实现Runnable接口的方式,多个线程可以共享同一个对象的数据和方法,因为多个线程共同操作的是同一个Runnable对象。 总结来说,继承Thread方式简单直接,但只能通过单继承来实现多线程;实现Runnable接口方式更灵活,可以解决单继承的限制,并且多个线程可以共享同一个Runnable对象的数据和方法。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ΘLLΘ

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值