Java面试题汇总(二)基础篇

面向对象三大特征

继承:子类通过继承父类除构造方法外的所有东西实现代码的复用。子类拥有父类非 private的属性和方法。
子类可以拥有自己属性和方法,即子类可以对父类进行扩展。
子类可以用自己的方式实现父类的方法。

封装:把一个对象的属性私有化,同时提供一些被外界访问的属性和方法。如果属性不想被外界访问,我们大可不必提供方法给外界访问。但是如果一个类没有提供给外界访问的方法,那么这个类也没有什么意义了。

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

什么是多态机制?Java语言是如何实现多态的?

所谓多态就是指程序中定义的引用变量所指向的具体类型和通过该引用变量发出的方法调用在编译时并不确定,而是在程序运行期间才确定,即一个引用变量倒底会指向哪个类的实例对象,该引用变量发出的方法调用到底是哪个类中实现的方法,必须在由程序运行期间才能决定。因为在程序运行时才确定具体的类,这样,不用修改源程序代码,就可以让引用变量绑定到各种不同的类实现上,从而导致该引用调用的具体方法随之改变,即不修改程序代码就可以改变程序运行时所绑定的具体代码,让程序可以选择多个运行状态,这就是多态性。

多态分为编译时多态和运行时多态。其中编辑时多态是静态的,主要是指方法的重载,它是根据参数列表的不同来区分不同的函数,通过编辑之后会变成两个不同的函数,在运行时谈不上多态。而运行时多态是动态的,它是通过动态绑定来实现的,也就是我们所说的多态性

Java实现多态有三个必要条件:继承、重写、向上转型。 继承:在多态中必须存在有继承关系的子类和父类。
重写:子类对父类中某些方法进行重新定义,在调用这些方法时就会调用子类的方法。
向上转型:在多态中需要将子类的引用赋给父类对象,只有这样该引用才能够具备技能调用父类的方法和子类的方法。

抽象类和接口的区别

抽象类是对类的抽象,是设计模板。接口是行为的抽象,是行为的规范。

相同点:
抽象类和接口不能被实例化
都包含抽象方法,其子类必须覆写这些抽象方法

不同点
在这里插入图片描述
![在这里插入图片描述](https://img-blog.csdnimg.cn/202005281708

这里是引用

19377.png)
Jdk1.7及其接口以前只能定义常量和抽象方法
1.8及其以后可以定义静态方法

抽象类能被final修饰吗

不能,final修饰的类不能被继承,而我们的抽象类是要被继承的,两者相矛盾

静态方法和实例方法的区别,以及在内存中存放的位置

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

静态方法在访问本类成员时只能访问静态成员,不能访问实例成员变量和方法,而实例方法无此限制.

静态方法在方法区/元空间,和类加载一同完成加载
实例方法的代码还是放在元空间/方法区:
静态方法和实例方法都是类加载的时候一起进入方法区/元空间。这里有个方法入口的概念,(加载到内存中有直接引用,要使用变量或是方法,都要有东西让jvm能找到才行)。静态方法是类加载就有了直接引用/入口地址,实例方法是创建的第一个对象会创建直接引用/入口地址,后边创建的对象就直接使用。这个是比较深入或是细节的。

成员变量和局部变量的区别

在这里插入图片描述
类变量被static修饰的变量,随着类的加载而加载,类的消失而消失(参考static的用法)

构造方法

1、 java中定义一个不干什么事儿的无参构造方法的作用

java在执行子类的构造方法之前,如果没有用super()来调用父类特定构造方法则会调用父类无参构造方法。因此如果父类只定义了有参构造方法,而在子类的构造方法中又没有super()来调用父类中特定构造方法,则编译时会报错

2、 构造方法的作用

帮助子类完成初始化工作

3、 构造方法的特性

方法名与类名相同
没有返回值,但不能用void声明
生成类对象是自动执行,无需调用

代码块

在这里插入图片描述

内部类

一个类定义到一个类的内部就叫内部类
分为静态内部类,成员内部类,局部内部类,匿名内部类

静态内部类:被static修饰的内部类

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();

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

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();

局部内部类:定义在方法里的内部类

定义在实例方法中的局部类可以访问外部类的所有变量和方法,定义在静态方法中的局部类只能访问外部类的静态变量和方法。

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

匿名内部类:没有名字的内部类

new/接口{ 
  //匿名内部类实现部分
}
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();
}

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

局部内部类和匿名内部类访问局部变量的时候,为什么变量必须要加上final?

因为 生命周期不一致 ,
局部变量直接存储在栈中,当方法执行结束后,非final的局部变量就被销毁。而局部内部类对局部变量的引用依然存在,如果局部内部类要调用局部变量时,就会出错。加了final,可以确保局部内部类使用的变量与外层的局部变量区分开,解决了这个问题。

重写与重载

在这里插入图片描述

hashCode() 与 equals()

equals()方法是属于java.lang包下Object的方法,如果该方法没有被重写默认是==,对于基本类型比较的是值,引用类型比较的是地址,所以我们要判断两个对象的内容是否相等,就要重写equals方法

hashCode()方法的作用是获取哈希码(散列码),它实际返回的是一个整数。哈希码的作用是确定该对象在哈希表中的索引位置

1、 为什莫要有HashCode()?

hashCode方法可以这样理解:它返回的就是根据对象的内存地址换算出的一个值。这样一来,当集合要添加新的元素时,先调用这个元素的hashCode方法,就一下子能定位到它应该放置的物理位置上。如果这个位置上没有元素,它就可以直接存储在这个位置上,不用再进行任何比较了;如果这个位置上已经有元素了,就调用它的equals方法与新元素进行比较,相同的话就不存了,不相同就散列其它的地址。这样一来实际调用equals方法的次数就大大降低了,几乎只需要一两次。提高了执行速度。

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

class Person {
    public int age;
    public String name;

    public Person(int age, String name) {
        this.age = age;
        this.name = name;
    }

    @Override
    public String toString() {
        return "Person{" +
                "age=" + age +
                ", name='" + name + '\'' +
                '}';
    }
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Person person = (Person) o;
        return age == person.age &&
                Objects.equals(name, person.name);
    }
    public static void main(String[] args) {
        Person person1=new Person(18,"liy");
        Person person2=new Person(18,"liy");
        Set<Person> set=new HashSet<>();
        set.add(person1);
        set.add(person2);
        System.out.println(person1.hashCode());
        System.out.println(person2.hashCode());
        System.out.println(person1.equals(person2));
        System.out.println(set);

    }
}

在这里插入图片描述

2、 hashCode()与equals()相关规定

如果两个对象相等,则hashCode()一定相等,调用equals方法一定返回true
如果两个对象hashCode相等,两个对象不一定相等

JDK 中常用的包有哪些

java.lang:这个是系统的基础类;
java.io:这里面是所有输入输出有关的类,比如文件操作等;
java.nio:为了完善 io 包中的功能,提高 io 包中性能而写的一个新包;
java.net:这里面是与网络有关的类;
java.util:这个是系统辅助类,特别是集合类;
java.sql:这个是数据库操作的类。

error和exception的关系和区别?

首先Exception和Error都是继承于Throwable 类,在 Java 中只有Throwable

类型的实例才可以被抛出(throw)或者捕获(catch),它是异常处理机制的基本组成类型。

Error是java程序运行中不可预料的异常情况,这种异常发生以后,会直接导致JVM不可处理或者不可恢复的情况。所以这种异常不可能抓取到,比如OutOfMemoryError、NoClassDefFoundError等。

Exception是java程序运行中可预料的异常情况,咱们可以获取到这种异常,并且对这种异常进行业务外的处理。其中的Exception又分为检查性异常和非检查性异常。两个根本的区别在于,检查性异常
必须在编写代码时,使用try catch捕获(比如:IOException异常)。非检查性异常

在代码编写使,可以忽略捕获操作(比如:ArrayIndexOutOfBoundsException),这种异常是在代码编写或者使用过程中通过规范可以避免发生的。

常见的运行时异常有哪些?

算数异常,类转换异常,数组越界异常,空指针异常

try-catch-finally的运行顺序?哪部分可以省略?

程序先执行 try 中的代码 如果 try 中的代码出现异常, 就会结束 try 中的代码, 看和 catch 中的异常类型是否匹配.
如果找到匹配的异常类型, 就会执行 catch 中的代码 * 如果没有找到匹配的异常类型, 就会将异常向上传递到上层调用者. *
无论是否找到匹配的异常类型, finally 中的代码都会被执行到(在该方法结束之前执行). * 如果上层调用者也没有处理的了异常,
就继续向上传递. 一直到 main 方法也没有合适的代码处理异常, 就会交给 JVM 来进行处理, 此时程序就会异常终止

try catch finally 中catch,或finally都可以省略,不过只能省略其中的一个

try-catch
try-finally
try-catch-finally

try与finally都有返回值,返回哪一个?

try 中的 return 语句调用的函数先于 finally 中调用的函数执行,也就是说 try 中的 return 语句先执行,finally 语句后执行,但try中的 return 并不是让函数马上返回结果,而是 return 语句执行后,将把返回结果放置进函数栈中,此时函数并不是马上返回,它要执行 finally 语句后才真正开始返回。 但此时会出现两种情况:

① 如果finally中也有return,则会直接返回finally中的return结果,并终止程序,函数栈中的return不会被完成

    private static int test1() {
        try {
            System.out.println(1/0);
            return -1;
        }catch (Exception e){
            return 1;
        }finally {
            System.out.println(0);
            return 2;

        }
    }
    //0
	//2

② 如果finally中没有return,则在执行完finally中的代码之后,会将函数栈中保存的try return的内容返回并终止程序

	private static int test() {
        try {
            System.out.println(1/0);
            return -1;
        }catch (Exception e){
            return 1;
        }finally {
            System.out.println(0);
        }
    }
    // 0
	//1

注意:
1、不管有没有出现异常,finally块中代码都会执行(try块中如果有System.exit(0);代码就会不执行finally块代码)
2、当try和catch中有return时,finally仍然会执行
3、finally是在try中return后面的表达式运算后执行的(此时并没有返回运算后的值,而是先把要返回的值保存起来,管finally中的代码怎么样,返回的值都不会改变,仍然是之前保存的值),所以函数返回值是在finally执行前确定的
4、finally中最好不要包含return,否则程序会提前退出,返回值不是try或catch中保存的返回值

猜你喜欢

转载自blog.csdn.net/qq_41552331/article/details/106408991
今日推荐