类的继承
继承在程序中复用一些已经定义完善的类,不仅可以减少软件开发周期,也可以提高软件的可维护性和可扩展性。
子类重写父类的方法:返回参数相同,方法名相同,传入参数相同,只有方法体不同,前提是具有父子关系。
所有类的构造方法 ,第一行都有一个隐藏的“super();" 作用是在执行该构造方法之前调用其父类的构造方法。
在Java语言中,一个类继承另一个类需要使用关键字extrends,关键字extrends的使用方法如下
class Child extrends Parent{}
创建子类对象,观察构造方法执行顺序,代码如下:
运行结果:
子类继承父类之后可以调用父类创建好的属性和方法。在电话基础上衍生出手机类,代码如下:
运行结果:
Object类
在开始学习使用class关键字定义类时,就应用到了继承原理,因为在Java中所有的类都直接或间接继承了java.lang.Object类是比较特殊的类,它是所有类的父类,是Java类层中的最高层类。
1、getClass()方法
getClass()方法是Object类定义的方法,它会返回对象执行时的Class实例,然后使用此实例调用getName()方法可以取得类的名称。语法如下:
getClass().getname();r
可以将getClass()方法与toString()方法联合使用。
2、toString()方法
toString()方法的功能是将一个对象返回为字符串形式,它会返回一String实例。在实际的应用中通常重写toString()方法,为对象提供一个特定的输出模式。当这个类转换为字符串或与字符串连接时,将自动调用重写的toString()方法。
让学生自我介绍,创建Child类,重写toString()方法,使该类的对象可以自定义输出自己的姓名和年龄,代码如下:
运行结果:
3、equals()方法
在Java语言中,有两种比较对象的方式,分别为”==“运算符与equals()方法。两者的区别在于:”==“比较的是两个对象引用内存地址是否相等,而equals()方法比较的是两个对象的实际内容。
根据身份证号判断是否为同一人,用equals()方法和”==“运算符来判断是否存在多个对象代表同一个人,代码如下:
运行结果:
对象类型的转换
1、向上转型
子类转父类,即子类赋值给父类,用自动类型转换
Bird bird=new Pigeon(); //用向上转换,借助自动类型转换,将鸽子类转换为鸟类,告诉编译器:“某只鸽子是一只鸟”
在运行向上转换的过程中,父类的对象无法调用子类独有的属性或者方法。
2、向下转型
父类转子类 ,即父类赋值给子类,用强制类型转换。语法如下:
子类类型 子类对象=(子类类型)父类对象;
方法的重载
方法名相同,参数类型(返回参数、传入参数)不同,参数个数不同,参数顺序不同,都能构成重载。
编写不同形式的加法运算方法。代码如下:
运行结果:
在谈到参数个数可以确定两个方法是否具有重载关系时,会想到定义不定义长参数方法。不定长方法的语法如下:
返回值 方法名(参数数据类型...参数名称)
使用不定长参数重载加法运算方法。代码如下:
运行结果:
final关键字
1、final变量用final修饰方法的不能被重写![](https://img-blog.csdnimg.cn/505c5d7068bb490b8a36912bc9b15061.png)
会报错,错误如下(常量PI不允许被修改)
当在程序中使用到PI这个常量时,它的值就是3.1415926。如果在程序中再次对定义为final的常量赋值,编译器将不会接受。
2、final方法
用final修饰变量不能被改变
将方法定义为final类型,可以防止子类修改父类的定义与现实方式,同时定义为final的方法的执行效果要高于非final方法。
3、final类
用final修饰类不能被继承
错误如下:
定义为final的类不能被继承。如果希望一个类不被任何类继承,并且不允许其他人对这个类进行任何改动,可以将这个类设置为final类。final类的语法如下:
final 类名{}
使用instanceof关键字判断对象类型
instanceof的语法格式如下:
对象名 instanceof 类名
判断对象是否属于该类或子类
使用instanceof关键字的表达式返回值为布尔类值。如果返回值为true,说明对象为类的实例对象;如果返回值为false,说明对象不是类的实例对象 。
分析几何图形之间的继承关系,代码如下:
错误如下:
因为四边形类与圆形类没有继承关系,因此两者不能使用instanceof关键字进行比较,否者会发生“不兼容”错误。
八,多态
例题7.12
7.3.1 方法的重载
方法的重载意义:在同一个类中允许同时存在一个以上的同名方法,只要这些方法的参数个数或类型不同即可。
重载与重写是两个完全不同的概念,重载主要用于一个类内实现若干重载的方法。这些方法的名称相同而参数形势不同;而重写主要用于子类继承父类时,重新实现父类中的非私有方法。
7.3.2 向上转型
把子类对象赋值给父类类型的变量,这种技术被称为“向上转型”。
在执行向上转型操作时,父类的对象无法调用子类独有的属性或者方法。
class Quadrangle { //四边形类
public static void draw( Quadrangle q) { //四边形类中的方法
//SomeSentence
}
}
public class Parallelogram extends Quadrangle { //平行四边形类,继承了四边形类
public class void main(String args[]) { //实例化平行四边形类对象引用
Parallelogram p = new Parallelogram();//调用父类方法
draw(p);
}
}
7.3.3 向下转型
通过向上转型可以推理出向下转型是将较抽象的类转换为较具体的类。
向下转型通常会出现问题:将父类对象直接赋予子类,会发生编译器错误,因为父类对象不一定是子类的实例。
越是具体的对象具有的特性越多,越抽象的对象具有的特性越少。
class Restaurant {
public static void draw( Restaurant q) {
//SomeSentence
}
}
public class parallelogram extends Restaurant {
public static void main(String[] args[]) {
draw(new parallelogram () );
//将平行四边形类对象看作是四边形对象,称为向上转型操作
Restaurant q = new parallelogram();
parallelogram p=q; //将父类对象赋予子类对象
//修改:parallelogram q= (parallelogram) q;
}
}
7.3.4 instanceof 关键字
myobject instanceof ExampleClass
class Quadrangle {
public static void draw(Quadrangle q) {
//SomeSentence
}
}
class Square extends Quadrangle {
//SomeSentence
class Anything {
//SomeSentence
public class parallelogram extends Quadrangle {
public static void main(String[] args[]) {
Quadrangle q = new Quadrangle //实例化父类对象
//判断父类对象是否为Parallelogram子类的一个实例
if (q instanceof Parallelogram) {
Parallelogram p = (Parallelogram) q; //进行向下转型操作
//判断父类对象是否为Parallelogram子类的一个实例
if (q instanceof Square) {
Square s = (Sauare) q; //进行向下转型操作
}
//由于q对象不为Aaything类的对象,所以这条语句是错误的
//System.out.println(q instanceof Anything);
}
}
注意:instanceof是Java语言的关键字,在Java中的关键字都为小写。
7.4 抽象类与接口
7.4.1 抽象类与抽象方法
在解决实际问题时,一般将父类定义为抽象类,需要使用这个父类进行继承与多态处理。
在Java语言中设置抽象类不可以实例化对象,因为图形类不能抽象出任何一种具体图像,但它的子类却可以。
[权限修饰符] abstract class 类名 {
类体
}
[权限修饰符] abstract 方法返回值类型 方法名(参数列表);
注意:构造方法不能定义为抽象方法。
public abstract class Market {
public String name; //商场名称
public String goods; //商场名称
public abstract void shop(); //抽象方法,用来输出信息
定义一个TaobaoMarket类,继承自Market抽象类,实现其中的shop抽象方法,代码如下:
public class TaobaoMarket extends Market {
@Override
public void shop() {
//TODO Auto-generated method stub
System.out.println(name+"网购"+goods);
}
}
定义一个WallMarket类,继承自Market抽象类
public class WallMarket extends Market {
@Override
public void shop() {
//TODO Auto-generated method stub
System.out.println(name+"实体店购买"+goods);
}
}
public class GOShopping
{
public stadtic void main(String[] args)
{
Market market = new WallMarket();//使用费派生类对象创建抽象对象
Market.name = "沃尔玛";
market.goods = "七匹狼西服";
market.shop =();
market = new TaobaoMarket();//使用费派生类对象创建抽象对象
market.name = "淘宝";
market.goods = "韩都衣舍花裙";
market.shop();
}
}
使用抽象类和抽象方法时需要遵守以下原则:
(1)在抽象类中,可以包含抽象方法,也可以不包含抽象方法,但是包含了抽象方法的类必须被定义为抽象类。
(2)抽象类不能直接实例化,即使抽象类中没有声明抽象方法,也不能实例化。
(3)抽象类被继承后,子类需要实现其中所有的抽象方法。
(4)如果继承抽象类的子类也被声明为抽象类,则可以不用实现父类中所有的抽象方法。
注意:构造方法不能定义为抽象方法。
7.4.2 接口的声明及实现
接口是抽象类的延申,可以将它看作是纯粹的抽象类,接口中的所有方法都没有方法体。
接口使用关键字interface进行定义,其语法如下:
[修饰符] interface 接口名 [extends 父接口名列表] {
[public] [static] [final] 常量;
[public] [abstract] 方法;
}
一个接口实现一个接口可以使用implements关键字,代码如下:
public class Parallelogram extends Quadrangle implements drawTest {
...//
}
7.6 内部类
7.6.1 成员内部类
1、成员内部类简介
在一个类中使用内部类,可以在内部类中直接存取其所在类的私有成员变量。
在内部类中可以随意使用外部类的成员方法以及成员变量。
成员内部类的语法如下:
public class OuterClass { //外部类
private class InnerClass { //内部类
//...
}
}
例如,在主方法中实例化一个内部类对象。
public class void main(String args[]) {
OuterClass out = new OuterClass();
OuterClass.innerClass in =out.doit();
OuterClass.innerClass in2=out.new innerClass(); //实例化内部类对象
、使用this关键字获取内部类与外部类的引用
如果在外部类中定义的成员变量与内部类的成员变量名称相同,可以使用this关键字。
使用成员内部类时,应该遵循以下原则:
(1)可以有各种修饰符,可以用private、public、protectd、static、final 、abstract等修饰符。
(2)如果有内部类有static限定,就是类级别的,否则为对象级别.类级别可以通过外部类直接访问,对象级别需要先生成外部类的对象后才能访问;
(3)内部类不能同名;
(4)非静态内部类中不能声明任何static成员;
(5)内部类可以互相调用;
public class TheSameName {
private int x;
private class Inner {
private int x = 9;
public void doit (int_x) {
x++; //调用的是形参x
this.x++; //调用内部类的变量x
TheSameName.this.x++; //调用外部类的变量x
}
}
}
7.6.2 局部内部类
局部类不仅可以在类中进行定义,也可以在类的局部位置定义,如在类的方法或任意的作用域中均可以定义内部类。
interface OutInterface2 {
}
class OuterClass3 {
public OutInterface2 doit (final String x) { //doit()方法参数为final类型
//在doit()方法中定义一个内部类
class InnerClass2 implements OutInterface2 {
InnerClass2(String s) {
s = x;
System.out.println(s);
}
}
return new InnerClass2("doit");
}
}
7.6.3 匿名内部类
匿名类所有实现代码都需要在大括号之间进行编写。语法如下:
return new A() { //A指类名
...//内部类体
};
使用匿名内部类时应该以下原则:
(1)匿名类没有构造方法
(2)匿名类不能定义静态的成员
(3)匿名类不能用private、public、protectd、static、final 、abstract等修饰
(4)只可以创建一个匿名类实例
7.6.4 静态内部类
在内部类前添加修饰符static ,这个内部类就变为静态内部类了。
静态内部类具有以下两个特点:
1)如果创建静态内部类的对象,不需要创建其外部类的对象;
(2)不能从静态内部类的对象中访问非静态外部类的对象。
public class StaticInnerClass {
int x = 100;
static class Inner {
void doitInner() {
//System.out.prinln("外部类"+x); //不能调用外部类的成员变量x
}
}
}
在静态内部类中定义主方法
+ public static void main(String args[]) {
System.out.prinln();
7.6.5 内部类的继承
内部类和其他普通类一样可以被继承,但是继承内部类比继承普通类复杂,需要设置专门的语法来完成。
public class OutputInnerClass extends ClassA.ClassB {
public OutputInnerClass(ClassA a) {
a.super();
}
}
class ClassA {
class ClassB {
}
}
在某个类继承内部类时,必须硬性给予这个类带参数的构造方法,并且该构造方法的参数必须是该内部类的外部类引用。