点赞再看,Java进阶一大半
在一次某大厂面试中,小南(化名)被面试官问到:写代码你什么时候选择用接口,什么时候用抽象类?
一位海外博客拥有24k粉丝的程序员博主是这么回答的,友们怎么看?
Java 接口和抽象类之间的选择取决于您的特定要求。如果您需要定义多个不相关的类需要遵循的契约,请使用接口。但是,如果您想创建具有共享实现细节的可重用基类,请选择抽象类。在某些情况下,您甚至可以同时使用两者,使用抽象类实现接口来执行契约并为相关类提供通用基础。请记住考虑每个选项的优缺点,为您的软件设计做出最佳选择。
大家好,我是南哥。
一个Java学习与进阶的领路人,相信对你通关面试、拿下Offer进入心心念念的公司有所帮助。
1. 抽象类
1.1 子类调用父类
现在有IDEA集成开发环境,可以给大家实时提醒哪个地方编译错误,但假如要大家用.txt
文件编写程序呢。南哥问:现在这段代码错在了哪?
class Base {
public Base(String s) {
System.out.print("B");
}
}
public class Derived extends Base {
public Derived (String s) {
System.out.print("D");
}
public static void main(String[] args) {
new Derived("C");
}
}
假如父类和子类同时拥有有参构造方法,子类的构造方法必须显性地调用父类的构造方法,否则会编译错误。所以正常的写法应该是这样。
代码语言:java复制 public Derived (String s) {
super(s);
System.out.print("D");
}
另外大家还需要注意一点,调用父类的构造方法必须在子类构造方法的第一行,调用父类的构造方法也只能出现在子类的构造方法上,否则也会是编译报错。
1.2 子类访问父类
如下代码,一共有两处编译错误。提示:错误在Child类里,能快速找出来吗?
代码语言:java复制class Parent {
public static String staticVar = "Static Variable from Parent";
private static String privateStaticVar = "Private Static Variable from Parent";
public static void staticMethod() {
System.out.println(staticVar);
}
private static void privateStaticMethod() {
System.out.println(privateStaticVar);
}
}
class Child extends Parent {
public void staticMethod() {
System.out.println("Static method in Child");
}
public void display() {
System.out.println(staticVar);
System.out.println(privateStaticVar);
privateStaticMethod();
staticMethod();
}
(1)父类的私有变量、私有方法,子类是有继承的,但是不能访问。所以Child.display()
里的以下调用是编译错误的。
System.out.println(privateStaticVar);
privateStaticMethod();
(2)子类可以继承,同时也可以访问父类的static变量、方法。但父类的static
方法大家需要注意,子类是不能直接覆盖的,所以以下代码会编译错误。
public void staticMethod() {
System.out.println("Static method in Child");
}
正确的做法是为该方法添加一个static修饰符,代表这是子类的一个新方法。这种写法叫做方法隐藏,子类和父类中都有一个相同名称和参数的静态方法时,子类的方法将隐藏父类的方法。
代码语言:java复制 public static void staticMethod() {
System.out.println("Static method in Child");
}
另外如果父类的方法使用final修饰,子类也是不能覆盖的。
1.3 父类不可访问的方法
紧跟着上文代码的例子,父类的方法同样使用static
修饰,子类的privateStaticMethod
方法算不算覆盖父类的方法呢?有没有编译报错?
class Parent {
public static String staticVar = "Static Variable from Parent";
private static String privateStaticVar = "Private Static Variable from Parent";
private static void privateStaticMethod() {
System.out.println(privateStaticVar);
}
}
class Child extends Parent {
public void privateStaticMethod() {
System.out.println(staticVar);
}
}
答案是编译正常。
父类中不可访问的方法,子类编写相同名称和参数的方法并不算覆盖。父类的方法都不能访问了,也就没有覆盖这一说法了。。。
2. 接口
2.1 访问修饰符的区别
接口和抽象类有三个方面的区别,分布是类的修饰、方法的修饰、变量的修饰。我们往下看看。
(1)类
接口使用interface
修饰,而抽象类使用abstract
修饰。当它们作为外部类时,只能使用public、default修饰,不能使用private修饰。
(2)方法
普通接口方法只能由public abstract
、default
、static
修饰。
抽象接口方法可以由所有修饰符修饰,除了final。
总结下,它们两者也有共同点,就是都不能使用final修饰。
(3)变量
普通接口变量只能由public static final
修饰。
抽象接口变量可以由所有修饰符修饰。
2.2 静态分派
这算是一个很偏的知识点了,如下代码有三个名为getType
的重载方法,它们的返回类型相同、方法名也相同,只有入参类型不同。
南哥问:程序执行结果是什么?
代码语言:java复制public class Test {
public static void main(String[] args) {
for(Collection<?> collection: collections) {
System.out.println(getType(collection));
}
}
public static final Collection<?>[] collections = {new HashSet<String>(), new ArrayList<String>()};
public static String getType(Collection<?> collection) {
return "Super:collection";
}
public static String getType(List<?> list) {
return "Super:list";
}
public String getType(ArrayList<?> list) {
return "Super:arrayList";
}
}
南哥给大家这么一行代码:Collection<?> collection = new ArrayList<Integer>()
,左边的Collection<?>
其实是静态类型,右边的new ArrayList<Integer>()
其实是动态类型。
而编译器在处理重载方法时,是根据参数的静态类型作为判断依据,而不是根据动态类型。collections
数组里面的所有实例的静态类型都是Collection<?>
,getType
方法也都是执行上文的第一个重载方法。
# 程序员执行结果
Super:collection
Super:collection
你学会(fei)了吗?
最后
在某网站上看到22届Java校招一片红海?24届今年的行情呢?
本文收录在我开源的《Java学习进阶指南》中,涵盖了在大厂工作的Javaer都不会不懂的核心知识、面试重点。相信能帮助到大家在Java成长路上不迷茫,南哥希望收到大家的 ⭐ Star ⭐支持我完善下去。GitHub地址:https://github.com/hdgaadd/JavaProGuide。
欢迎关注南哥的公众号:Java进阶指南针。公众号里有南哥珍藏整理的大量优秀pdf书籍!
我是南哥,南就南在Get到你的有趣评论➕点赞➕关注。
我正在参与2024腾讯技术创作特训营最新征文,快来和我瓜分大奖!
创作不易,不妨点赞、收藏、关注支持一下,各位的支持就是我创作的最大动力❤️