java基础常见的面试题总结

本文深入解析Java中的重载与重写、String类特性、自动装箱与拆箱、==与equals方法、String创建方式、封装、继承、多态、接口、抽象类、基本与引用类型区别、static与final关键字等关键概念。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1、重载和重写的区别

重载(overload):发生在一个类中,有两个或者两个以上的方法名相同,其方法参数不同的方法。参数不同可以是参数顺序不同、个数不同、类型不同,在参数不同的基础上,方法的返回类型或者修饰符也可以不同。编译时,java平台会根据传入的参数类型来判断调用哪个方法。

注意:参数顺序相同,而返回类型或者修饰符不同,不是重载。java平台就会不知道该调用哪个方法

public class Overload {
	public void doit(int val,String str) {
		//方法体
	}
	public void doit(String str,int val) {
		//方法体
	}
	void doit(int val,String str,String str1) {
		//方法体
	}
	public String doit(int val1,int val2,String str1,String str2) {
		return str1;
	}
}

重写(override):发生在子类继承父类,子类重写父类的方法,方法名与参数必须相同,可见性(修饰符)必须大于等于父类,返回值必须是父类方法中返回类型的子类或者与父类相同。父类中被private修饰的方法只能在类范围内使用,无法重写,但是可以在子类中重新定义一个与父类private修饰的方法相同的方法,此时不是重写。父类中的静态方法可以重写,但是必须还是静态方法。父类中被final修饰的方法不能重写。

class Parent {
	protected void doit1() {
		//方法体
	}
	public Object doit2() {
		return new Object();
	}
	private void doit3() {
		//方法体
	}
}
class Child extends Parent {
	public void doit1() {//可见性大于父类方法
		//方法体
	}
	public String doit2() {
		return new String();//返回值为父类方法返回值的子类
	}
	private void doit3() {//此时不是重写 而是重新定义了一个相同名的方法
		//方法体
	}
}

2、String、StringBuffer、StringBuilder的区别是什么?

就字符串内容是否可变来说,String类型是不可变的字符串,在String类中,使用了final关键字修饰字符数组,一旦创建就不能被更改。而StringBuffer和StringBuilder都是AbstractStringBuilder类,其字符数组没有被final修饰,是可变的字符串。就线程安全来说,String类型是不可变的,可以说是线程安全的,StringBuffer类中的许多方法均被synchronized关键字修饰,是线程安全的,而StringBuilder中的方法未被synchronized修饰,是线程不安全的。从性能上讲,每次对String类型进行改变时,都会生成一个新的String对象,该引用就会指向新的String对象,StringBuilder和StringBuffer均是对其本身进行操作。操作少量的数据时,推荐使用String类型,单线程时,使用StringBuilder,多线程时,使用StringBufffer。

3、自动装箱与自动拆箱

在java中有八种基本数据类型:char、boolean、byte、short、int、long、float、double,而java是一种面向对象的语言,在很多地方只能使用对象而不是基本数据类型,因此分别封装了这八种基本数据类型的引用类型:Character、Boolean、Byte、short、Integer、Long、Float、Double,为了方便起见便提供了自动封装和自动装箱的功能,

可以利用valueof()方法实现装箱、xxxValue()实现拆箱,以int型为例

int intValue();将Integer值作为int
staic Integer value(int i);//返回指定Integer指定的int值的Integer实例

自动装箱(boxing):把基本数据类型转换为对应的包装类型

自动拆箱(unboxing):将包装类型转化为基本数据类型

int val = 20;
ArrayList<Integer> array = new ArrayList<>();//该数组中只能添加Integer型对象
array.add(val);//int型变量会自动装箱为Integer
val = array.get(0);//Integer型对象又会自动拆箱为int型变量

4、==、equals方法与hashcode方法

==:对于基本数据类型和字符串来说,是比较值是否相等;对于对象类型来说,是比较两个引用是否是指向同一个对象

public class test {
	public static void main(String[] args) {
		int val1 = 20;
		int val2 = 20;
		Object obj1 = new Object();
		Object obj2 = new Object();
		System.out.println(val1 == val2);
		System.out.println(obj1 == obj2);
	}
}

//输出
true
false

equals()和hashcode()均是Object类中的方法,equals()方法比较的是两个引用是否指向同一个对象,即比较两个引用指向的地址是否相同;hashcode()会根据对象所在的内存地址返回一个能够唯一代表这个对象的int值。

public class test {
	public static void main(String[] args) {
		Object obj1 = new Object();
		Object obj2 = new Object();
		System.out.println(obj1.equals(obj2));
		System.out.println(obj1.hashCode());
		System.out.println(obj2.hashCode());
	}
}
//输出
false
366712642
1829164700

Object类中的Object类是所有类的父类,因此所有类都会继承方法,但是通常会对equals()和hashCode()这两个方法进行重写,重写equals()后,比较两个对象的内容是否相等,重写hashcode()后会保证两个相同的对象的哈希值相等例如String类就重写了这两个方法。

public class test {
	public static void main(String[] args) {
		String str1 = new String("abc");
		String str2 = new String("abc");
		System.out.println(str1.equals(str2));
		System.out.println(str1.hashCode());
		System.out.println(str2.hashCode());
	}
}
//输出
true
96354
96354

一般情况下,不涉及hash类型集合时候,只需要重写equals(),不需要重写hashcode(),主要原因是:hashMap、HashSet、HashTable等会通过对象的哈希值来判断两个对象是否相等,所以必须重写hashCode(),以免导致两个两个内容相同的对象被存储两遍。

5、String类两种创建方法的区别

String str1 = "string";//方式一常量式创建
String str2 = "string";
String str3 = new String("string");//方式二对象式创建
System.out.println(str1 == str2);
System.out.println(str1 == str3);
System.out.println(str1.equals(str2));
System.out.println(str1.equals(str3));
//输出
true
false
true
true

对于第一种创建方式,在创建字符串常量时,JVM会检查字符串常量池是否存在”string“字符串,如果不存在,就会在常量池中开辟一个内存空间存放"string",变量名str1是在栈中,保存的是“string”的地址;如果存在,在不必再在常量池中创建一个“string”字符串,变量名str2保存的也是“string”的地址,因此str1和str2都是指向的是同一个对象,第一个输出是true(等号是判断两个引用是否是指向同一个对象)

对于第二种创建方式,在创建时,JVM也会先检查字符串常量池是否存在“string”字符串,如果不存在,则会在常量池中开辟一二个内存空间存放“string”,如果不存在,则不需要重新再开辟空间。然后在内存堆中开辟空间存放new出的String实例指向的是”string“,栈中保存的是引用str3指向new的String实例。因此str1是指向”string“的,而str3是指向new String的,第二个输出是false。而equals()在String类中是比较的内容是否相等,因此第三和第四个输出均为true。

6、关于封装

将同类事物的共性封装在一个类中,在类的外部无法直接访问该类,只能通过该类的实例化对象或者类名进行访问。封装遵循“开闭原则”,禁止外部直接访问和修改类的信息,可通过修饰符来限制类和类中方法的可视性。

  • 类变量:即静态变量,该变量被static修饰,属于类范畴,只能通过类名进行访问;一旦类变量被修改,那么该类的所有对象中该变量均会被修改
  • 成员变量:在类定义时声明的变量,会随着对象的建立而被简历,对象消失也会随之消失,属于对象范畴,存在于对象所在的堆内存中
  • 局部变量:在函数中声明的变量,只定义在局部范围,存在于栈内存中,作用的范围结束,栈帧释放,变量也就随之消失了
  • 成员方法:声明在类体中的方法,被static修饰的方法,属于类范围,通过类名访问,其余属于对象范围,通过对象访问
  • 修饰符
 所在类同个包中的类可见对子类可见对不在同个包中的类可见
public
protected×
默认××
private×××

 注意:pretected对不是同一个包中不可见,但是若是其子类,是可见的;default对同包中的子类也是可见的

  • 构造函数:是用来初始化对象的,通过构造函数创造一个对象,一个类中可以有多个构造函数(重载),构造函数的方法名与类名相同。需要注意的是,在类中若没有声明构造函数,java会默认声明一个无参构造函数;若声明了构造函数,若想使用无参构造则必须自己声明

7、关于继承 extends

继承是类与类的一种关系,子类继承父类所有属性和方法(private修饰的方法不会被继承),以此来实现带代码的复用。

关于继承需要注意的几点问题:

  • 父类中private修饰的方法的可见性仅在本类中,因此不会被继承
  • 父类中被final修饰的方法,在子类中不能重写
  • 子类可以重写父类中的方法,返回值必须与父类中方法的返回值类型相同或者为该类型的子类,修饰符的可见性必须大于等于父类。
  • 父类中被final修饰的方法,不能重写
  • 父类中被static修饰的方法,若在子类中重写该方法,必须用static修饰

8、关于多态

多态实际上就是同种事物的多种状态,主要包括引用多态和方法多态

  • 引用多态:使用父类引用接收子类对象,即向上转型;向下转型,只能通过强制转换,且只有父类的引用指向的是子类的对象,才能够强转。需要注意对象的实际类型决定调用的方法
public class test1 {
	public static void main(String[] args) {
		// TODO 自动生成的方法存根
		Parent parent = new Child();//向上转型
		parent.doit();
		Child child = (Child)parent;//向下转型,parent实际上是子类对象 才能用子类的引用接收
		child.doit();
	}
}
class Parent{
	public void doit() {
		System.out.println("父类方法");
	}
}

class Child extends Parent{
	public void doit() {
		System.out.println("子类重写父类中的方法");
	}
}

//输出
子类重写父类中的方法
子类重写父类中的方法
  •  方法多态:主要是子类重写父类中的方法,会根据引用所指向的对象决定调用子类还是父类中的方法;重载:根据参数决定调用哪一个方法

9、关于接口 implements interface(JDK8)

由于在java中只能单继承,为了弥补该缺陷,引入接口,接口可以多继承。接口可以有成员变量、局部变量和成员方法

  • 成员变量:默认被final修饰,同时也能被public 和 static修饰,用static修饰时,只能通过接口名来访问
  • 局部变量:定义在方法体内的变量
  • 成员方法:若没有方法体,则默认为抽象方法;若有方法体,则可以被default、static修饰,被statci修饰时,只能通过接口名访问
public interface Interface {
	int val1 = 20;//默认被final修饰
	public int val2 = 30;
	static int val3 = 40;//仅能通过接口访问
	public default void doit1() {
		System.out.println("接口中的default方法,通过接口实现类对象访问");
	}
	public static void doit2() {
		System.out.println("接口中的静态方法,只能通过接口名访问");
	}
	public abstract void doit3();
	public void doit4();//默认为抽象方法
}

 注意:通过类实现接口,类中如果没有实现接口中的方法,那么这个类必须变成抽象类,否则就添加要实现的方法

10、抽象类

抽象类是被abstract修饰的类,相比于普通类,区别在于:抽象类可以定义抽象方法,定义了抽象方法的类一定是抽象类;

抽象类不能实例化对象。与接口相似,继承抽象类后,要么实现其中的抽象方法,要么将其变为抽象类。

特点如下:

  • 抽象的
  • 抽象类不一定有抽象方法,但类中有抽象方法,只能是抽象类
  • 抽象类不能new创建对象,但抽象类的引用可以接收实现了其抽象方法的子类对象
  • 使用关键字abstract修饰抽象方法和抽象类
  • 若其子类不重写抽象方法,那么子类也必须是抽象方法

11、java中基本类型与引用类型的区别

基本类型:char、boolean、byte、short、int、long、float、double,保存的直接是原始值

引用类型:保存的是引用值,是指实例对象在堆中的地址

由于Java是面向对象的语言,java为八大基本类型封装了对应的引用类型,以及自动拆箱和自动装箱。

12、static关键字

  • 修饰变量:静态变量,在类加载时完成初始化
  • 修饰方法:在类加载时就存在,可以访问静态变量,但是不能访问非静态变量
  • 修饰代码块:在类加载完之后就会执行代码块中的内容。执行顺序:父类中的静态代码块->子类静态代码块->父类构造方法->子类非静态代码块->子类构造方法

被static修饰的方法和变量称为静态方法和静态变量,属于类范围,一般情况下,通过类名进行访问。静态方法和静态变量是在类加载时决定的,因此在static方法中,是不能访问非静态方法和变量的。需要注意的是,在子类重写父类中的静态方法时,该方法也只能是静态的。静态代码块也将只执行一次。静态代码块是在类加载时执行的

public class staticTest {
    public static void main(String[] args){
        A obj = new A();
        A.doit();
    }
}
class A{
    static int value = 20;
    int value2 = 10;
    //静态代码块
    static{
        System.out.println("测试静态代码块");
    }
    //静态方法
    public static void doit(){
        int value = A.value;//在静态方法中可以访问静态变量,但是不能访问非静态变量
        System.out.println("测试静态方法");
    }
    //普通方法
    public void doit1(){
        System.out.println("测试普通方法");
    }
}

//运行结果
测试静态代码块
测试静态方法
测试普通方法

13、final关键字

  • 修饰变量:定义时候完成赋值,且只能赋值一次,不能再修改
  • 修饰方法:不能被重写
  • 修饰类:不能被继承
  • 修饰形参:不可变

优点:

  • JVM和Java应用都会缓存final变量,提高了性能
  • 可以安全的在多线程环境下进行共享,而不需要额外的同步开销
  • 使用final关键字,JVM会对方法、变量及类进行优化

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值