首頁  >  文章  >  Java  >  2020全新java基礎面試題彙總

2020全新java基礎面試題彙總

王林
王林轉載
2020-10-21 16:51:034496瀏覽

2020全新java基礎面試題彙總

java==和equals和hashCode的區別

(更多面試題推薦:java面試題及答案

1、==

java中的資料類型,可分為兩類:

  1. #基本資料類型,也稱為原始資料類型 byte,short,char,int,long,float,double,boolean  他們之間的比較,應用雙等號(==),比較的是他們的值。

  2. 引用型別(類別、介面、陣列) 當他們用(==)進行比較的時候,比較的是他們在記憶體中的存放位址,所以,除非是同一個new出來的對象,他們的比較後的結果為true,否則比較後結果為false。物件是放在堆中的,堆疊中存放的是物件的參考(位址)。先看下虛擬機器記憶體圖和程式碼:

public class testDay {
    public static void main(String[] args) {
        String s1 = new String("11");
        String s2 = new String("11");
        System.out.println(s1 == s2);
        System.out.println(s1.equals(s2));
    }
}

結果是:

false
true

2020全新java基礎面試題彙總

s1和s2都分別儲存的是對應物件的位址。所以如果用 s1== s2時,比較的是兩個物件的位址值(即比較引用是否相同),為false。而呼叫equals方向的時候比較的是對應位址裡面的值,所以值為true。這裡就需要詳細描述下equals()了。

2、equals()方法詳解

equals()方法是用來判斷其他的物件是否和該物件相等。其再Object裡面就有定義,所以任何一個物件都有equals()方法。差別在於是否重寫了該方法。

先看下原始碼:

public boolean equals(Object obj) {    return (this == obj);
}

很明顯Object定義的是兩個物件的位址值的比較(即比較引用是否相同)。但為什麼String裡面呼叫equals()卻是比較的不是位址而是堆記憶體位址裡面的值。這裡就是個重點了,像String 、Math、Integer、Double等這些封裝類別在使用equals()方法時,已經覆寫了object類別的equals()方法。看下String裡面重寫的equals():

public boolean equals(Object anObject) {    if (this == anObject) {        return true;
    }    if (anObject instanceof String) {
        String anotherString = (String)anObject;
        int n = value.length;        if (n == anotherString.value.length) {
            char v1[] = value;
            char v2[] = anotherString.value;
            int i = 0;            while (n-- != 0) {                if (v1[i] != v2[i])                    return false;
                i++;
            }            return true;
        }
    }    return false;
}

重寫了之後就是這是進行的內容比較,而已經不再是先前位址的比較。依序類別推Math、Integer、Double等這些類別都是重寫了equals()方法的,從而進行的是內容的比較。當然,基本型別是進行值的比較。

要注意的是當equals()方法被override時,hashCode()也要被override。依照一般hashCode()方法的實作來說,相等的對象,它們的hashcode一定相等。為什麼會這樣,這裡又要簡單提hashcode了。

3、hashcode()淺談

明明是java中==和equals和hashCode的區別問題,怎麼一下子又扯到hashcode()上面去了。你一定很鬱悶,好了,我打個簡單的例子你就知道為什麼==或equals的時候會牽扯到hashCode。

舉例說明下:如果你想找出一個集合中是否包含某個對象,那麼程式該怎麼寫呢?不要用indexOf方法的話,就是從集合去遍歷然後比較是否想到。萬一集合中有10000個元素呢,累屎了吧。所以為了提高效率,哈希演算法也就產生了。核心思想就是將集合分成若干個儲存區域(可以看成一個個桶),每個物件可以計算出一個雜湊碼,可以根據雜湊碼分組,每組分別對應某個儲存區域,這樣一個物件根據它的哈希碼就可以分到不同的儲存區域(不同的區域)。

所以再比較元素的時候,其實是先比較hashcode,如果相等了之後才去比較equal方法。

看下hashcode圖解:

2020全新java基礎面試題彙總

#一個物件一般有key和value,可以根據key來計算它的hashCode值,再根據其hashCode值儲存在不同的儲存區域中,如上圖。不同區域能儲存多個值是因為會牽涉到hash衝突的問題。簡單如果兩個不同物件的hashCode相同,這種現象稱為hash衝突。簡單來說就是hashCode相同但是equals不同的值。對於比較10000個元素就不需要遍歷整個集合了,只需要計算要查找對象的key的hashCode,然後找到該hashCode對應的存儲區域查找就over了。

2020全新java基礎面試題彙總

大概可以知道,先通过hashcode来比较,如果hashcode相等,那么就用equals方法来比较两个对象是否相等。再重写了equals最好把hashCode也重写。其实这是一条规范,如果不这样做程序也可以执行,只不过会隐藏bug。一般一个类的对象如果会存储在HashTable,HashSet,HashMap等散列存储结构中,那么重写equals后最好也重写hashCode。

总结:

  1. hashCode是为了提高在散列结构存储中查找的效率,在线性表中没有作用。
  2. equals重写的时候hashCode也跟着重写
  3. 两对象equals如果相等那么hashCode也一定相等,反之不一定。

2. int、char、long 各占多少字节数

byte 是 字节

bit 是 位

1 byte = 8 bit

char在java中是2个字节,java采用unicode,2个字节来表示一个字符

short 2个字节

int 4个字节

long 8个字节

float 4个字节

double 8个字节

3. int和Integer的区别

  1. Integer是int的包装类,int则是java的一种基本数据类型
  2. Integer变量必须实例化后才能使用,而int变量不需要
  3. Integer实际是对象的引用,当new一个Integer时,实际上是生成一个指针指向此对象;而int则是直接存储数据值
  4. Integer的默认值是null,int的默认值是0

延伸: 关于Integer和int的比较

  1. 由于Integer变量实际上是对一个Integer对象的引用,所以两个通过new生成的Integer变量永远是不相等的(因为new生成的是两个对象,其内存地址不同)。
Integer i = new Integer(100);
Integer j = new Integer(100);
System.out.print(i == j); //false
  1. Integer变量和int变量比较时,只要两个变量的值是向等的,则结果为true(因为包装类Integer和基本数据类型int比较时,java会自动拆包装为int,然后进行比较,实际上就变为两个int变量的比较)
Integer i = new Integer(100);
int j = 100;
System.out.print(i == j); //true
  1. 非new生成的Integer变量和new Integer()生成的变量比较时,结果为false。(因为非new生成的Integer变量指向的是java常量池中的对象,而new Integer()生成的变量指向堆中新建的对象,两者在内存中的地址不同)
Integer i = new Integer(100);
Integer j = 100;
System.out.print(i == j); //false
  1. 对于两个非new生成的Integer对象,进行比较时,如果两个变量的值在区间-128到127之间,则比较结果为true,如果两个变量的值不在此区间,则比较结果为false
Integer i = 100;
Integer j = 100;
System.out.print(i == j); //true
Integer i = 128;
Integer j = 128;
System.out.print(i == j); //false

对于第4条的原因: java在编译Integer i = 100 ;时,会翻译成为Integer i = Integer.valueOf(100);,而java API中对Integer类型的valueOf的定义如下:

public static Integer valueOf(int i){
    assert IntegerCache.high >= 127;    if (i >= IntegerCache.low && i <p>java对于-128到127之间的数,会进行缓存,Integer i = 127时,会将127进行缓存,下次再写Integer j = 127时,就会直接从缓存中取,就不会new了</p><h2 class="heading" data-id="heading-6">4. java多态的理解</h2><h3 class="heading" data-id="heading-7">1.多态概述</h3><ol>
<li><p>多态是继封装、继承之后,面向对象的第三大特性。</p></li>
<li><p>多态现实意义理解:</p></li>
</ol>
  • 现实事物经常会体现出多种形态,如学生,学生是人的一种,则一个具体的同学张三既是学生也是人,即出现两种形态。

  • Java作为面向对象的语言,同样可以描述一个事物的多种形态。如Student类继承了Person类,一个Student的对象便既是Student,又是Person。

  1. 多态体现为父类引用变量可以指向子类对象。

  2. 前提条件:必须有子父类关系。

注意:在使用多态后的父类引用变量调用方法时,会调用子类重写后的方法。

  1. 多态的定义与使用格式

定义格式:父类类型 变量名=new 子类类型();

2.多态中成员的特点

  1. 多态成员变量:编译运行看左边

Fu f=new Zi();

System.out.println(f.num);//f是Fu中的值,只能取到父中的值

  1. 多态成员方法:编译看左边,运行看右边

Fu f1=new Zi();

System.out.println(f1.show());//f1的门面类型是Fu,但实际类型是Zi,所以调用的是重写后的方法。

3.instanceof关键字

作用:用来判断某个对象是否属于某种数据类型。

* 注意: 返回类型为布尔类型

使用案例:

Fu f1=new Zi();
Fu f2=new Son();if(f1 instanceof Zi){
    System.out.println("f1是Zi的类型");
}else{
    System.out.println("f1是Son的类型");
}

4.多态的转型

多态的转型分为向上转型和向下转型两种

  • 向上转型:多态本身就是向上转型过的过程

    • 使用格式:父类类型 变量名=new 子类类型();

    • 适用场景:当不需要面对子类类型时,通过提高扩展性,或者使用父类的功能就能完成相应的操作。

  • 向下转型:一个已经向上转型的子类对象可以使用强制类型转换的格式,将父类引用类型转为子类引用各类型

    • 使用格式:子类类型 变量名=(子类类型)父类类型的变量;

    • 适用场景:当要使用子类特有功能时。

5.多态案例:

例1:

package day0524;
 
public class demo04 {
    public static void main(String[] args) {
        People p=new Stu();
        p.eat();
        //调用特有的方法
        Stu s=(Stu)p;
        s.study();
        //((Stu) p).study();
    }
}
class People{
    public void eat(){
        System.out.println("吃饭");
    }
}
class Stu extends People{
    @Override
    public void eat(){
        System.out.println("吃水煮肉片");
    }
    public void study(){
        System.out.println("好好学习");
    }
}
class Teachers extends People{
    @Override
    public void eat(){
        System.out.println("吃樱桃");
    }
    public void teach(){
        System.out.println("认真授课");
    }
}

答案:吃水煮肉片 好好学习

例2:

请问题目运行结果是什么?

package day0524;
public class demo1 {
    public static void main(String[] args) {
        A a=new A();
        a.show();
        B b=new B();
        b.show();
    }
}
class A{
    public void show(){
        show2();
    }
    public void show2(){
        System.out.println("A");
    }
}
class B extends A{
    public void show2(){
        System.out.println("B");
    }
}
class C extends B{
    public void show(){
        super.show();
    }
    public void show2(){
        System.out.println("C");
    }
}

答案:A B

5. String、StringBuffer和StringBuilder区别

1、长度是否可变

  • String 是被 final 修饰的,他的长度是不可变的,就算调用 String 的concat 方法,那也是把字符串拼接起来并重新创建一个对象,把拼接后的 String 的值赋给新创建的对象
  • StringBuffer 和 StringBuilder 类的对象能够被多次的修改,并且不产生新的未使用对象,StringBuffer 与 StringBuilder 中的方法和功能完全是等价的。调用StringBuffer 的 append 方法,来改变 StringBuffer 的长度,并且,相比较于 StringBuffer,String 一旦发生长度变化,是非常耗费内存的!

2、执行效率

  • 三者在执行速度方面的比较:StringBuilder > StringBuffer > String

3、应用场景

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

StringBuffer和StringBuilder区别

1、是否线程安全

  • StringBuilder 类在 Java 5 中被提出,它和 StringBuffer 之间的最大不同在于 StringBuilder 的方法不是线程安全的(不能同步访问),StringBuffer是线程安全的。只是StringBuffer 中的方法大都采用了 synchronized 关键字进行修饰,因此是线程安全的,而 StringBuilder 没有这个修饰,可以被认为是线程不安全的。

2、应用场景

  • 由于 StringBuilder 相较于 StringBuffer 有速度优势,所以多数情况下建议使用 StringBuilder 类。
  • 然而在应用程序要求线程安全的情况下,则必须使用 StringBuffer 类。 append方法与直接使用+串联相比,减少常量池的浪费。

6. 什么是内部类?内部类的作用

内部类的定义

将一个类定义在另一个类里面或者一个方法里面,这样的类称为内部类。

内部类的作用:

  1. 成员内部类 成员内部类可以无条件访问外部类的所有成员属性和成员方法(包括private成员和静态成员)。 当成员内部类拥有和外部类同名的成员变量或者方法时,会发生隐藏现象,即默认情况下访问的是成员内部类的成员。

  2. 局部内部类 局部内部类是定义在一个方法或者一个作用域里面的类,它和成员内部类的区别在于局部内部类的访问仅限于方法内或者该作用域内。

  3. 匿名内部类 匿名内部类就是没有名字的内部类

  4. 静态内部类 指被声明为static的内部类,他可以不依赖内部类而实例,而通常的内部类需要实例化外部类,从而实例化。静态内部类不可以有与外部类有相同的类名。不能访问外部类的普通成员变量,但是可以访问静态成员变量和静态方法(包括私有类型) 一个 静态内部类去掉static 就是成员内部类,他可以自由的引用外部类的属性和方法,无论是静态还是非静态。但是不可以有静态属性和方法

(学习视频推荐:java课程

7. 抽象類別和介面的差異

  1. 抽象類別要被子類別繼承,介面要被類別實作。
    2020全新java基礎面試題彙總
  2. 介面只能做方法聲明,在抽象類別中可以作方法聲明,也可以做方法實作。
    2020全新java基礎面試題彙總
  3. 介面裡定義的變數只能是公共的靜態的常數,抽象類別中的變數是普通變數。
    2020全新java基礎面試題彙總
  4. 介面是設計的結果,抽象類別是重構的結果。
    2020全新java基礎面試題彙總
  5. 抽象類別和介面都是用來抽象特定物件的,但是介面的抽象層級最高。
    2020全新java基礎面試題彙總
  6. 抽象類別可以有具體的方法和屬性,介面只能有抽象方法和不可變常數。
    2020全新java基礎面試題彙總
  7. 抽象類別主要用來抽象類別,介面主要用來抽像功能。
    2020全新java基礎面試題彙總

8. 抽象類別的意義

抽象類別: 一個類別中如果包含抽象方法,這個類別應該用abstract關鍵字聲明為抽象類別。

意義:

  1. 為子類別提供一個公共的型別;
  2. 封裝子類別中重複內容(成員變數與方法) ;
  3. 定義有抽象方法,子類別雖然有不同的實現,但該方法的定義是一致的。

9. 抽象類別與介面的應用場景

1.介面(interface)的應用場合:

  1. 類別與類別之間需要特定的介面進行協調,而不在乎其如何實現。
  2. 作為能夠實現特定功能的標識存在,也可以是什麼介面方法都沒有的純粹標識。
  3. 需要將一組類別視為單一的類,而呼叫者只透過介面來與這組類別發生聯繫。
  4. 需要實現特定的多項功能,而這些功能之間可能完全沒有任何關聯。

2.抽象類別(abstract.class)的應用場合:

一句話,在既需要統一的接口,又需要實例變數或缺省的方法的情況下,就可以使用它。最常見的有:

  1. 定義了一組接口,但又不想強迫每個實作類別都必須實作所有的介面。可以用abstract.class定義一組方法體,甚至可以是空方法體,然後由子類別選擇自己感興趣的方法來覆寫。
  2. 某些場合下,只靠純粹的介面無法滿足類別與類別之間的協調,也必需類別中表示狀態的變數來區別不同的關係。 abstract的中介作用可以很好地滿足這一點。
  3. 規範了一組相互協調的方法,其中一些方法是共同的,與狀態無關的,可以共享的,無需子類分別實現;而另一些方法卻需要各個子類根據自己特定的狀態來實現特定的功能

10.  抽象類別是否可以沒有方法和屬性?

答案是:可以在

抽象類別中可以沒有抽象方法,但有抽象方法的一定是抽象類別。所以,java中 抽象類別裡面可以沒有抽象方法。注意即使是沒有抽象方法和屬性的抽象類,也不能被實例化。

11. 接口的意义

  1. 定义接口的重要性:在Java编程,abstract class 和interface是支持抽象类定义的两种机制。正是由于这两种机制的存在,才使得Java成为面向对象的编程语言。
  2. 定义接口有利于代码的规范:对于一个大型项目而言,架构师往往会对一些主要的接口来进行定义,或者清理一些没有必要的接口。这样做的目的一方面是为了给开发人员一个清晰的指示,告诉他们哪些业务需要实现;同时也能防止由于开发人员随意命名而导致的命名不清晰和代码混乱,影响开发效率。
  3. 有利于对代码进行维护:比如你要做一个画板程序,其中里面有一个面板类,主要负责绘画功能,然后你就这样定义了这个类。可是在不久将来,你突然发现现有的类已经不能够满足需要,然后你又要重新设计这个类,更糟糕是你可能要放弃这个类,那么其他地方可能有引用他,这样修改起来很麻烦。如果你一开始定义一个接口,把绘制功能放在接口里,然后定义类时实现这个接口,然后你只要用这个接口去引用实现它的类就行了,以后要换的话只不过是引用另一个类而已,这样就达到维护、拓展的方便性。
  4. 保证代码的安全和严密:一个好的程序一定符合高内聚低耦合的特征,那么实现低耦合,定义接口是一个很好的方法,能够让系统的功能较好地实现,而不涉及任何具体的实现细节。这样就比较安全、严密一些,这一思想一般在软件开发中较为常见。

12. Java泛型中的extends和super理解

在平时看源码的时候我们经常看到泛型,且经常会看到extends和super的使用,看过其他的文章里也有讲到上界通配符和下届通配符,总感觉讲的不够明白。这里备注一下,以免忘记。

  1. extends也成为上界通配符,就是指定上边界。即泛型中的类必须为当前类的子类或当前类。
  2. super也称为下届通配符,就是指定下边界。即泛型中的类必须为当前类或者其父类。

这两点不难理解,extends修饰的只能取,不能放,这是为什么呢? 先看一个列子:

public class Food {}
public class Fruit extends Food {}
public class Apple extends Fruit {}
public class Banana extends Fruit{}

public class GenericTest {

    public void testExtends(List extends Fruit> list){

        //报错,extends为上界通配符,只能取值,不能放.
        //因为Fruit的子类不只有Apple还有Banana,这里不能确定具体的泛型到底是Apple还是Banana,所以放入任何一种类型都会报错
        //list.add(new Apple());

        //可以正常获取
        Fruit fruit = list.get(1);
    }

    public void testSuper(List super Fruit> list){

        //super为下界通配符,可以存放元素,但是也只能存放当前类或者子类的实例,以当前的例子来讲,
        //无法确定Fruit的父类是否只有Food一个(Object是超级父类)
        //因此放入Food的实例编译不通过
        list.add(new Apple());
//        list.add(new Food());

        Object object = list.get(1);
    }
}

在testExtends方法中,因为泛型中用的是extends,在向list中存放元素的时候,我们并不能确定List中的元素的具体类型,即可能是Apple也可能是Banana。因此调用add方法时,不论传入new Apple()还是new Banana(),都会出现编译错误。

理解了extends之后,再看super就很容易理解了,即我们不能确定testSuper方法的参数中的泛型是Fruit的哪个父类,因此在调用get方法时只能返回Object类型。结合extends可见,在获取泛型元素时,使用extends获取到的是泛型中的上边界的类型(本例子中为Fruit),范围更小。

总结:在使用泛型时,存取元素时用super,获取元素时,用extends。

13. 父类的静态方法能否被子类重写

不能,父类的静态方法能够被子类继承,但是不能够被子类重写,即使子类中的静态方法与父类中的静态方法完全一样,也是两个完全不同的方法。

class Fruit{

    static String color = "五颜六色";
    static public void call() {
        System.out.println("这是一个水果");
    }
}

public class Banana extends Fruit{

    static String color = "黄色";
    static public void call() {
        System.out.println("这是一个香蕉");
    }

    public static void main(String[] args) {

        Fruit fruit = new Banana();
        System.out.println(fruit.color);    //五颜六色
        fruit.call();         //这是一个水果
    }
}

如代码所示,如果能够被重写,则输出的应该是这是一个香蕉。与此类似的是,静态变量也不能够被重写。如果想要调用父类的静态方法,应该使用类来调用。 那为什么会出现这种情况呢? 我们要从重写的定义来说:

重写指的是根据运行时对象的类型来决定调用哪个方法,而不是根据编译时的类型。

对于静态方法和静态变量来说,虽然在上述代码中使用对象来进行调用,但是底层上还是使用父类来调用的,静态变量和静态方法在编译的时候就将其与类绑定在一起。既然它们在编译的时候就决定了调用的方法、变量,那就和重写没有关系了。

静态属性和静态方法是否可以被继承

可以被继承,如果子类中有相同的静态方法和静态变量,那么父类的方法以及变量就会被覆盖。要想调用就就必须使用父类来调用。

class Fruit{

    static String color = "五颜六色";
    static String xingzhuang = "奇形怪状";
    static public void call() {
        System.out.println("这是一个水果");
    }
    static public void test() {
        System.out.println("这是没有被子类覆盖的方法");
    }
}

public class Banana extends Fruit{

    static String color = "黄色";
    static public void call() {
        System.out.println("这是一个香蕉");
    }

    public static void main(String[] args) {

        Banana banana = new Banana();    
        banana.test();     //这是没有被子类覆盖的方法
        banana.call();     //调用Banana类中的call方法    这是一个香蕉
        Fruit.call();         //调用Fruit类中的方法 这是一个水果

        System.out.println(banana.xingzhuang + " " + banana.color);   //奇形怪状 黄色
    }
}

从上述代码可以看出,子类中覆盖了父类的静态方法的话,调用的是子类的方法,这个时候要是还想调用父类的静态方法,应该是用父类直接调用。如果子类没有覆盖,则调用的是父类的方法。静态变量与此相似。

14. 執行緒與行程的差異

  • 定義面向:行程是程式在某個資料集合上的一次執行活動;執行緒是行程中的一個執行路徑。 (行程可以建立多個執行緒)
  • 角色方面:在支援執行緒機制的系統中,行程是系統資源分配的單位,執行緒是CPU調度的單位。
  • 資源共享方面:進程之間不能共享資源,而執行緒共享所在行程的位址空間和其它資源。同時執行緒還有自己的堆疊和堆疊指針,程式計數器等暫存器。
  • 獨立性方面:進程有自己獨立的位址空間,而執行緒沒有,執行緒必須依賴進程而存在。
  • 開銷方面。進程切換的開銷較大。線程相對較小。 (前面也提到過,引入線程也出於了開銷的考慮。)

可看下這篇文章:juejin.im/post/684490…

15. final,finally,finalize的區別

  • final 用於聲明屬性,方法和類別, 分別表示屬性不可變, 方法不可覆寫, 類別不可繼承.
  • finally 是異常處理語句結構的一部分,表示總是執行.
  • finalize 是Object類別的一個方法,在垃圾收集器執行的時候會呼叫被回收物件的此方法,可以覆寫此方法提供垃圾收集時的其他資源回收,例如關閉文件等.

16. 序列化Serializable和Parcelable的區別

#Android中Intent如果要傳遞類別對象,可以透過兩種方式實現。

方式一:Serializable,要傳遞的類別實作Serializable介面傳遞對象, 方式二:Parcelable,要傳遞的類別實作Parcelable介面傳遞物件。

Serializable(Java自帶):Serializable是序列化的意思,表示將物件轉換成可儲存或可傳輸的狀態。序列化後的物件可以在網路上傳輸,也可以儲存到本地。 Serializable是一種標記接口,這意味著無需實作方法,Java就會對這個物件進行高效的序列化操作。

Parcelable(Android 專用):Android的Parcelable的設計初衷是因為Serializable效率過慢(使用反射),為了在程式內不同元件間以及不同Android程式間(AIDL)高效的傳輸資料而設計,這些資料僅在記憶體中存在。 Parcelable方式的實作原理是將一個完整的物件分解,而分解後的每一部分都是Intent所支援的資料類型,這樣也就實現傳遞物件的功能了。

效率及選擇:

Parcelable的效能比Serializable好,因為後者在反射過程頻繁GC,所以在記憶體間資料傳輸時建議使用Parcelable,如activity間傳輸資料。而Serializable可將資料持久化方便保存,所以在需要保存或網路傳輸資料時選擇Serializable,因為android不同版本Parcelable可能不同,所以不建議使用Parcelable進行資料持久化。 Parcelable不能使用在要將資料儲存在磁碟上的情況,因為Parcelable不能很好的保證資料的持續性在外界有變化的情況下。儘管Serializable效率低點,但此時還是建議使用Serializable 。

透過intent傳遞複雜資料型別時必須先實作兩個介面之一,對應方法分別是getSerializableExtra(),getParcelableExtra()。

17. 靜態屬性和靜態方法是否可以被繼承?是否可以被重寫?以及原因?

父類別的靜態屬性和方法可以被子類別繼承

#不可以被子類別重寫:當父類別的引用指向子類時,使用物件呼叫靜態方法或靜態變量,是呼叫的父類別中的方法或變數。並沒有被子類改寫。

原因:

因為靜態方法從程式開始運行後就已經分配了內存,也就是說已經寫死了。所有引用到該方法的物件(父類別的物件也好子類別的物件也好)所指向的都是同一塊記憶體中的數據,也就是該靜態方法。

子類別中如果定義了相同名稱的靜態方法,並不會重寫,而應該是在記憶體中又分配了一塊給子類別的靜態方法,沒有重寫這一說。

18 .java中靜態內部類的設計意圖

內部類別

#內部類,即定義在一個類別的內部的類別。為什麼有內部類別呢?

我們知道,在java中類別是單繼承的,一個類別只能繼承另一個具體類別或抽象類別(可以實作多個介面)。這種設計的目的是因為在多重繼承中,當多個父類別中有重複的屬性或方法時,子類別的呼叫結果會含糊不清,因此用了單繼承。

而使用內部類別的原因是:每個內部類別都能獨立地繼承一個(介面的)實現,所以無論外圍類別是否已經繼承了某個(介面的)實現,對於內部類別都沒有影響。

在我們程式設計中有時候會存在一些使用介面很難解決的問題,這個時候我們可以利用內部類別提供的、可以繼承多個具體的或是抽象的類別的能力來解決這些程式設計問題。可以這樣說,介面只是解決了部分問題,而內部類別使得多重繼承的解決方案變得更加完整。

靜態內部類別

在說靜態內部類別之前,先了解下成員內部類別(非靜態的內部類別)。

成員內部類別

成員內部類別也是最普通的內部類,它是外圍類別的一個成員,所以它可以無限制的存取外圍類別的所有成員屬性和方法,儘管是private的,但是外圍類別要存取內部類別的成員屬性和方法則需要透過內部類別實例來存取。

在成員內部類別中要注意兩點:

  • 成員內部類別中不能存在任何static的變數和方法;

  • 成員內部類別是依附於外圍類別的,所以只有先建立了外圍類別才能夠建立內部類別。

靜態內部類別

靜態內部類別與非靜態內部類別之間存在著最大的差異:非靜態內部類別在編譯完成之後會隱含地保存著一個引用,該引用是指向創建它的外圍內,但是靜態內部類別卻沒有。

沒有這個引用就意味著:

  • 它的創建是不需要依賴外圍類別的。

  • 它不能使用任何外圍類別的非static成員變數和方法。

其它兩種內部類別:局部內部類別與匿名內部類別

#局部內部類別

局部內部類別是嵌套在方法和作用域內的,對於這個類別的使用主要是應用與解決比較複雜的問題,想創建一個類別來輔助我們的解決方案,到那時又不希望這個類別是公共可用的,所以就產生了局部內部類,局部內部類別和成員內部類別一樣被編譯,只是它的作用域發生了改變,它只能在該方法和屬性中被使用,出了該方法和屬性就會失效。

匿名內部類別

  1. 匿名內部類別是沒有存取修飾符的。

  2. new 匿名內部類,這個類別首先是要存在的。

  3. 當所在方法的形參需要被匿名內部類別使用,那麼這個形參就必須為final。

  4. 匿名內部類別沒有明面上的建構方法,編譯器會自動產生一個引用外部類別的建構方法。

相關推薦:java入門

以上是2020全新java基礎面試題彙總的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:juejin.im。如有侵權,請聯絡admin@php.cn刪除