写点什么

先收藏!关于 Java 类、接口、枚举的知识点大汇总

发布于: 2021 年 02 月 22 日

摘要:Java 知识点精选之类、接口、枚举 30 问,算是比较基础的,希望大家一起学习进步。


Q: 各修饰符所代表的可见性?

public: 可被所有使用

protect: 只能被自己和子类使用,或者同一个包路径

private: 只能自己使用,儿子都不行

不加修饰符即 default 权限: 包访问权限,和他在同一包内的类都可以访问他,包外的则都不能访问


Q: 外部类可以用 private 或者 protect 修饰吗?

A: 不能,只能用 public 或者包访问权限。 内部类可以。


解释以下 final 的作用

Q: final 成员?

A: 如果是基本类型,则指值不能被改变。 如果是对象,指对象的引用不可改变,但是引用处的内容可改变。


Q: final 参数?

A: 参数不可变,只能读不能修改,同上


Q: final 方法

A: 方法不能被子类重写。


Q: final 类

A: 该类不能被继承。


Q:

final 局部变量可以作为非 final 的参数传入吗?会编译报错吗?


public static void main(String[] args){    final A a = new A();    changeA(a);}public void changeA(A a) {    // change A...}
复制代码


A:

可以作为非 final 的参数传入,不会编译报错。


Q: 重载和重写的区别?

A:

重载是方法名相同,参数不同。

重写是方法参数等都一致的情况下重写父类的方法。


Q: 如果子类重载了父类中的方法, 那么子类中还能调用父类中的同名方法吗?

A: 可以(C++中不可以调用父类中的同名重载方法)。


Q: 怎样能避免子类在重写父类的方法,不小心弄成了重载?

(即你想重写父类的 f(int), 却不小心写成了 f(int,int),导致调用 f(int)时还是调用了父类的 f ,怎么能避免这种失误?)

A: 加个 @Override 关键字即可,原文解释:



Q:父类的成员变量能被重写/覆盖嘛?


class A{    public String name = "A";}class B extends A{    public String name = "B";}public static void main{    A a = new B();   System.out.println(a.name);}
复制代码


A:

输出 A。

注意成员变量不具有多态性,因此你定义的是 A,赋值的是 B, 那么输出的依旧是 A 里的成员。

如果是被重写的方法的话,那会用 B 里的方法。


Q:内部类是啥,内部类能访问外部类的成员吗?

A:

内部类概念:


class A {    class B{           ...   }}
复制代码


B 就是 A 的内部类,B 能访问 A 的所有成员


Q: A 中有 1 个内部类 C, A 的子类 B 中也有 1 个内部类 C, B 中的 C 会覆盖 A 中的 C 吗?

A: 不会, 因为使用时是通过 B.C 或者 A.C 去调用的,存在命名空间的关系。


Q:可以在内部类中定义静态成员吗?


class A {    class B{        static int b;           ...   }}
复制代码


A:

不可以。 除非在 class B 前面加 static 变为静态类


Q: 匿名类是啥, 匿名类能访问外面的变量或者对象吗?

A: 匿名类概念:


return new A(构造参数){   {构造器内容}   类定义 }
复制代码


匿名类如果要用外面的对象, 外面的对象必须要定义为 final。


Q: 嵌套类是啥,能访问外部类的成员吗?

A:


class A {   static int sa;   int a;    static class B{}}
复制代码


B 只能访问 A 中的静态成员 sa, 而不能访问 a。


[§] 接口


类是单继承,接口可以多继承


Q: 接口中如果要定义成员变量,那成员的默认修饰符是什么?

A: public static final


Q: 接口中各方法的默认修饰符是什么?

A: public abstract


Q: 接口中可以定义实现具体方法嘛?

A:

java8 以上版本可以。

引入了 default 关键字,在接口中用 default 关键字修饰接口,就可以在接口中去实现这个接口了。


[§] 枚举


Q: enum 可以被继承吗?

像下面这样:


enum A extend B{...}
复制代码


A: 不可以。enum 标识符本身被编译器处理过,自身就继承自 Enum 类,而 java 不支持多重继承。但支持实现接口



Q: switch(enum)时需要加 default 吗?

A: 可以不需要。


Q: Enum 基类里实现了 values()方法吗?

A: 没有实现, values 方法是编译器加的。因此从 List<Enum>里取出的对象,是不能调用 values()的。



Q:enum 里的枚举的默认修饰符默认是?

A:static final


[§]静态分派和动态分派


Q: 下面输出什么,属于什么分派?


 void test() {        Father father = new Son();          //静态分派        print(father);    }     void print(Father father) {        System.out.println("this is father");    }     void print(Son son) {        System.out.println("this is son");    }
复制代码


A:

输出 this is father。 属于静态分派。

静态分派概念: 编译器就能确定调用哪个方法。

这里 2 个 print 属于重载方法,通过输入参数的定义类型即立刻确定调用哪个

静态分派取决于静态类型


静态类型概念: 编译期写在 java 文件里能马上看到的类型

例如 A a = Factory.create(args);

那么左边的 A 就是静态类型, 而右边的类型取决于运行期,是不确定的。


Q: 上一题的进阶:


 public class Overload {    private static void sayHello(char arg){      System.out.println("hello char");    }    private static void sayHello(Object arg){      System.out.println("hello Object");    }    private static void sayHello(int arg){      System.out.println("hello int");    }    private static void sayHello(long arg){      System.out.println("hello long");    }    // 测试代码    public static void main(String[] args) {      sayHello('a');    }  }
复制代码


输出什么?

A:

输出 hello char

因为‘a’是一个 char 类型数据(即静态类型是 char),所以会选择参数类型为 char 的重载方法。

若注释掉 sayHello(char arg)方法,那么会输出 hello int

因为‘a’除了可代表字符串,还可代表数字 97。因此当没有最合适的 sayHello(char arg)方式进行重载时,会选择第二合适(第二优先级)的方法重载,即 sayHello(int arg)


总结:当没有最合适的方法进行重载时,会选优先级第二高的的方法进行重载,如此类推。

优先级顺序为:char>int>long>float>double>Character>Serializable>Object>…

其中…为变长参数,将其视为一个数组元素。变长参数的重载优先级最低。

因为 char 转型到 byte 或 short 的过程是不安全的,所以不会选择参数类型为 byte 或 short 的方法进行重载,故优先级列表里也没有。


Q: 下面输出什么,属于什么分派:


 void test() {        Father father = new Son();               father.name();        }     class Son extends Father {        void name(){            System.out.println("son");        }    }     class Father {        void name(){            System.out.println("father");        }    }
复制代码


A:

输出 son,属于动态分派。运行的时候根据所指向的具体对象才确定调用哪个方法


Q:


静态分派属于单分派还是多分派?

动态分派属于单分派还是多分派?

A:

静态分派是多分派。

动态分派是单分派。

多分派概念: 分派时即要考虑调用者的类型,也要考虑参数类型。

而单分派只考虑调用者的类型。


动态分派原理:


Q:

类方法在 class 文件中是什么样的? 是符号引用还是直接引用?

A:

class 文件中, 所定义的方法 都只是符号引用,即只是个符号,知道方法名字, 但是不知道方法的实际指令运行地址

符号引用如下,不过我这展示的时 class_info 即类的符号引用, 实际上还会有 method_info 即方法的引用:



然后方法在 class 文件中时这样存放的, 先是一个 method_count 数量,接着再存储方法。



此时是不知道方法的指令地址的。 除非从符号引用转为直接引用。


Q:什么时候方法的符号引用会转为实际方法区中的直接引用?

A:

类加载的解析阶段会把满足「编译期可知,运行期不可变」的方法的符号引用替换为指向方法区的直接引用,不会延迟到运行时再去完成。


构造

私有

静态

final 修饰

上面这 4 类方法在类加载时都会被识别出来,并转成 指向方法区的直接引用(即能知道了指令地址了,而不是字节码的符号)


Q:动态分派(即多态), 虚拟机里是怎么确定调用哪个方法的?

如下, 他怎么确定调用的是 Son 实现的 do, 还是 father 实现的 do?


int a = 1;Father f = new Son()f.do(a);
复制代码


A:

首先,通过静态分派, 他知道一定选用的是 f(int a) 这个方法,但是他不知道选用哪个类的 do(int a)方法。

而你执行 f.do(a)时, 操作数栈上会存放一个对象引用



那么执行 f 方法的虚拟机指令就会通过这个对象引用,找到他的实际类型的 class



他会在这个实际类中查找是否有实现这个方法,具体看 class 文件中有没有这个方法的定义



如果没有找到,他就去父类找,父类的关系 class 文件中就可以知道



如果父类没有,就接着往上找,直到找到实现了的。



本文分享自华为云社区《Java 知识点问题精选之类、接口、枚举》,原文作者:breakDraw 。


点击关注,第一时间了解华为云新鲜技术~


发布于: 2021 年 02 月 22 日阅读数: 18
用户头像

提供全面深入的云计算技术干货 2020.07.14 加入

华为云开发者社区,提供全面深入的云计算前景分析、丰富的技术干货、程序样例,分享华为云前沿资讯动态,方便开发者快速成长与发展,欢迎提问、互动,多方位了解云计算! 传送门:https://bbs.huaweicloud.com/

评论

发布
暂无评论
先收藏!关于Java类、接口、枚举的知识点大汇总