20145215《Java程序设计》第4周学习总结
20145215《Java程序设计》第四周学习总结
教材学习内容总结
继承与多态
继承
- 继承作为面向对象的第二大特征,基本上就是避免多个类间重复定义共同行为。即当多个类中存在相同属性和行为时,将这些内容抽取到单独一个类中,那么多个类无需再定义这些属性和行为,只要继承单独的那个类即可。我们也把单独抽取出来的那个类称为父类,其他的多个类称为子类。
- 在Java中,通过
extends
关键字让类与类之间产生继承关系,例如教材中SwordsMan继承Role的例子:
public class SwordsMan extends Role{
public void fight(){
System.out.println("挥剑攻击");
}
}
其中,SwordsMan就是子类,Role就是父类(Role的代码在教材159页已给出)。
- 子类可以直接访问父类中的非私有的属性和行为,而对于private成员也会被继承,只不过子类无法直接存取,必须通过父类提供的方法来存取(如果父类愿意提供方法的话)。
- Java只支持单继承,不支持多继承,即子类只能继承一个父类,例如
public class SwordsMan extends Role1
是可行的,但是public class SwordsMan extends Role1,Role2...
这种继承就是不可行的,子类与父类之间会有is-a
的关系,例如:
Role role1=new SwordsMan();
Role role2=new Magician();
也是可以通过编译的,因为在编译程序时,计算机的读取方式是从=号右边往左读,即要检查右边是不是一种左边(是否为is-a的关系),上例中SwordsMan是一种Role,所以语法逻辑正确,编译也就成功了。当然,如果加上扮演(Cast)语法,编译程序会让程序代码通过编译,不过很有可能会扮演失败,执行时抛出ClassCastException
的错误。
- 虽然Java中不支持多继承,但是支持多层继承,例如:
class A{}
class B extends A{}
class C extends B{}
这种继承方法也是可行的。
- 继承的特点:继承的出现提高了代码的复用性,也让类与类之间产生了关系,提供了多态的前提。
多态
- 多态从字面上的解释就是某一类事物的多种存在形态,以抽象讲法解释就是使用单一接口操作多种类型的对象。例如:
public class RPG2
{
public static void main(String[] args)
{
SwordsMan1 swordsMan = new SwordsMan1();
swordsMan.setName("Justin");
swordsMan.setLevel(1);
swordsMan.setBlood(200);
Magician1 magician = new Magician1();
magician.setName("Monica");
magician.setLevel(1);
magician.setBlood(100);
showBlood(swordsMan);
showBlood(magician);
}
static void showBlood(Role1 role)
{
System.out.printf("%s 血量 %d%n",role.getName(),role.getBlood());
}
}
在showBlood()方法中,既可以通过Role类型操作SwordsMan对象,也可以通过Role类型操作Magician对象,这就是多态。
- 父类或者接口的引用指向或者接收自己的子类对象,这便是多态的体现,多态的存在提高了程序的扩展性和后期可维护性,但是多态也需要有前提条件,首先,需要存在继承或者实现关系,其次还要有覆盖操作。面向对象的三大特征可以这么来描述:封装是继承的基础,继承是多态的基础。
- 如果某方法区块中没有任何程序代码操作,可以使用
abstract
标示该方法为抽象方法,该方法不用撰写{}区块,直接“;”结束即可。类中若有方法没有操作,并且标示为abstract
,表示这个类定义不完整,因此也就不能用来生成实例。Java中规定内含抽象方法的类,一定要在class
前标示abstract
,表示这是一个定义不完整的抽象类。
函数覆盖
- 子类中出现与父类一模一样的方法时,会出现覆盖操作。一般来说,当子类需要父类的功能,而功能主体子类有自己特有内容时,可以覆盖父类中的方法,这样,即沿袭了父类的功能,又定义了子类特有的内容。
- 父类中的私有方法不可以被覆盖,在子类覆盖方法中,继续使用被覆盖的方法可以通过super.函数名获取。
- 覆盖需要注意的是:覆盖时,子类方法权限一定要大于等于父类方法权限,并且静态只能覆盖静态。
继承语法细节
protected
成员:被声明为protected
的成员,相同包中的类可以直接存取,不同包中的类可以在继承后的子类直接存取。例如下面的例子:假如只想让子类可以直接存取name、level、与blood,就可以定义他们为protected
。
public abstract class Role5
{
protected String name;
protected int level;
protected int blood;
public int getBlood()
{
return blood;
}
public void setBlood(int blood)
{
this.blood = blood;
}
public int getLevel()
{
return level;
}
public void setLevel(int level)
{
this.level = level;
}
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
}
-
在Java中有
public
、protected
、private
三个权限关键字,但实际上有四个权限范围:
-
如果想取得父类中的方法定义,可以在调用方法前,加上super关键字,重新定义的时候要注意,对于父类中的方法权限,只能扩大不能缩小。如果想执行父类中某构造函数,可以使用super()指定,但要注意的是,this()和super()只能选择其中之一调用,并且一定要在构造函数第一行执行。
-
如果
class
前使用了final
关键字定义,那么表示这个类是最后一个,不会再有子类,即不能被继承。如果定义类时没有使用extends
关键字指定继承任何类,那一定是继承java.lang.Object
,在Java中,最上层的父类就是java.lang.Object
。
接口与多态
接口
- 对于“定义行为”,可以使用
interface
关键字定义,例如public interface Swimmer
,接口中的方法不能操作,直接表示为abstract
,而且一定是public
。类要操作接口,必须使用implements
关键字。操作某接口时,对接口中定义的方法有两种处理方式,一是操作接口中定义的方法,二是再度将该方法标示为abstract
,例如:
public abstract class Fish implements Swimmer {
protected String name;
public Fish(String name){
this.name = name;
}
public String getName(){
return name;
}
@Override
public abstract void swim();
}
- 操作接口会有“拥有行为”关系,而继承是“是一种”的关系,对于接口多态语法的判断,方式是“右边是不是拥有左边的行为”,或者“右边对象是不是操作了左边接口”。
- 类可以操作两个以上的类,可以同时继承某个类,并操作某些接口,接口的出现将“多继承”通过另一种形式体现出来,即“多实现”。
接口语法细节
- 使用
interface
来定义抽象的行为外观,方法要声明为public abstract
,无须且不能有操作。为了方便,也可以省略public abstract
,编译程序会协助补齐。可以使用接口枚举常数,只能定义为public static final
。为了方便,也可以省略public static final
。 - 接口可以继承别的接口,也可以同时继承两个以上的接口,也是使用
extends
关键字。 enum
语法可用于定义枚举常数,enum
实际上定义了类,而enum
中列举的常数实际上是public static final
,无法撰写程序直接实例化枚举类型,因为构造函数权限设定为private,只有类中才可以实例化。
教材学习中的问题和解决过程
- 教材上对于覆盖的讲解并不是很详细,一开始只知道Override大概有什么作用,但是对一些需要注意的地方还不太清楚,特别是在自己编代码用到Override的时候,弹出了错误,后来结合视频以及网上查的资料,对Override有了更多了解。
- 在用记事本编写完教材202页上Airplane的代码后,将它放到ch04文件夹里,然后用IDEA打开时,发现编译不能通过:
一开始不太明白为什么会报错,后来仔细一想,在编写的时候,我没有用package进行管理,但是我后来移到ch04\src文件夹里了,因此我在代码最前面加了一个package 04,问题得以解决。
代码调试中的问题和解决过程
- 随着学习的深入,可能很多时候我们都会出现类名重复的问题,如下图:
我的解决办法是在类名后加上数字编号,对于一些比较常用的类名,我们可能会多次定义,这样一来编译就无法通过,因此我们最好在每次编写的时候就给他们编好号。
- 下面是我自己编的一个测试代码:
public class OverrideTest {
public static void main(String[] args) {
}
}
class SuperClass1 {
public void f() {
}
}
class SubClass1 extends SuperClass1 {
@Override
public int f() {
return 1;
}
}
在编译时报错:
后来查Override
的基本概念,发现子类中不允许出现与父类同名同参但是不同返回类型的方法,于是我将下面的int
改成了void
,把return 1
也换成了System.out.println("Hello");
编译通过,在此补充Override的一个注意事项:子类覆盖父类的方法的访问权限要大于或者等于父类中被覆盖的方法,比如说父类的访问权限为public
,那么子类的访问权限也要为public
,而不能为private
。
心得体会
这周的学习更像是上周学习的一个延续,在学习Java之前,对于面向对象这个概念,我只有一个非常模糊的认识,但是通过这两周的学习,我对于面向对象的理解逐渐深入。当然,在这过程中,除了自己的努力,还有他人的帮助也是非常关键的。我现在也开始慢慢明白为什么老师引导我们去评论他人的博客,互动是非常重要的,书上学习到的知识只是硬知识,其他的软知识是需要我们自己在实践中一点一滴去摸索的,而评论他人的博客就是增长我们的软知识的一个过程。当你看到他人在实践过程中所犯的一些错误时,你会想如果自己遇到这种情况应该怎么办,一方面在以后的编程过程中,你会尽量去避免这种错误,另一方面,当你遇到这种错误的时候也不至于束手无策。此外,这周的另一个收获就是我开始以书上的例子为参考,自己尝试着去编写一些其他的程序,以此来检测我对知识点的掌握程度,而不再是像前几周一样,只是按照书上的代码原封不动的敲上去。这样一来,我发现当你自己编出来代码的时候,这种成就感是和之前完全不一样的。虽然目前学习Java只有短短的四周,但每周学习过程中的一些感触都让我受益匪浅,我想我大概开始逐渐享受这种自主性的学习了吧!
学习进度条
代码行数(新增/累积) | 博客量(新增/累积) | 学习时间(新增/累积) | 重要成长 | |
---|---|---|---|---|
目标 | 5000行 | 30篇 | 400小时 | 编写了Hello Java代码 |
第一周 | 100/100 | 2/2 | 12/12 | 编写了Hello Java代码 |
第二周 | 200/300 | 2/4 | 15/27 | 理解了printf和println的区别 |
第三周 | 450/750 | 1/5 | 22/49 | 对对象有了更深层次的理解 |
第四周 | 869/1619 | 1/6 | 28/77 | 对对象的三大特征有了更全面的认识 |
本周学习的代码已经使用IDEA成功托管:
利用wc统计代码行数如下: