6.面向对象编程(下)2
1:final关键字(掌握)
(1)final的含义
final是最终的意思,可以修饰类,方法,变量。
(2)特点
被final修饰的类、变量和方法将具有以下特性。
●final修饰的类不能被继承。
●final修饰的方法不能被子类重写(也叫覆盖)。
●final修饰的变量不能被重新赋值。
A:被final修饰的类,不能被继承。
举例子:
final class Fu {
}
class Zi extends Fu {// 错误: 无法从最终Fu进行继承
}
class FinalDemo {
public static void main(String[] args){
}
}
B:被final修饰的方法,该方法是不能被重写的。
举例子:
继承中方法有一个现象:方法重写。
父类的功能,就会被子类给覆盖掉。
但是被final修饰的方法是不可以被重写!
class Fu {
public final void show(){
System.out.println("Fu show");
}
}
class Zi extends Fu {
//被final修饰的方法是不可以被重写的
public void show(){ // error,Zi中的show()无法覆盖Fu中的show()
System.out.println("Zi show");
}
}
class ZiDemo {
public static void main(String[] args){
Zi z = new Zi();
z.show();
}
}
C:被final修饰的变量不能被重新赋值。因为这个变量其实就是常量。
举例子:
class Fu {
final public int num = 10;
}
class Zi extends Fu {
public void show(){
System.out.println(num);
//num = 100; // 错误: 无法为最终变量num分配值
System.out.println(num);
}
}
class FinalDemo {
public static void main(String[] args){
Zi z = new Zi();
z.show();
}
}
(3)经典案例
1:final修饰局部变量
●在方法内部,该变量不可以被改变。
●在方法声明上,分别演示基本类型和引用类型作为参数的情况
a: 局部变量是基本类型,基本数据类型的值不能发生改变
b: 局部变量是引用类型 地址值不能发生改变,但是对象的内容是可以改变的
举例子:
a:final修饰的局部变量为基本数据类型时
class FinalTest {
public static void main(String[] args){
//局部变量是基本数据类型
int x = 10;
x = 100;
System.out.println(x);//100
final int y = 10;
//错误: 无法为最终变量y分配值
//y = 100;
System.out.println(y);//10
}
}
举例子:
b:final修饰的局部变量为引用类型的数据时
class Student {
int age = 10;
}
class FinalTest {
public static void main(String[] args){
//局部变量是引用数据类型
Student s = new Student();
System.out.println(s.age);//10
s.age = 100;
System.out.println(s.age);//100
System.out.println("--------------");
final Student ss = new Student();
System.out.println(ss.age);//10
ss.age = 100;
System.out.println(ss.age);//100
//重新分配内存空间
//ss = new Student();// 错误: 无法为最终变量ss分配值
//对象的地址不能变,但是对象里面的内容却是可以变的
}
}
2:final修饰变量的初始化时机
a: 被final修饰的变量只能赋值一次(只能初始化一次)。
b: final修饰变量的初始化时机:在构造方法完毕前。(引申:针对非静态的常量)
举例子:
例1:
class Demo {
int num = 10;
final int num2 = 20;
public Demo(){
num = 100;
// 错误: 无法为最终变量num2分配值
//num2 = 200;
}
}
class FinalTest1 {
public static void main(String[] args){
Demo d = new Demo();
System.out.println(d.num);//100,做初始化先做成员变量初始化,再做构造函数初始化
System.out.println(d.num2);//20
}
}
例2:(思考下为什么例1会报错,可是例2不会报错)
class Demo {
int num;
final int num2;
public Demo(){
num = 100;
num2 = 200;
}
}
class FinalTest2 {
public static void main(String[] args){
Demo d = new Demo();
System.out.println(d.num);//100
System.out.println(d.num2);//200
}
}
例3:(思考:为什么例3 又报错了呢。总结:final修饰变量的初始化时机)
class Demo {
int num ;
final int num2 ;
{
num2 = 10;
}
public Demo(){
num = 100;
//无法为最终变量num2分配值
//num2 = 200;
}
}
class FinalTest3 {
public static void main(String[] args){
Demo d = new Demo();
System.out.println(d.num);//100
System.out.println(d.num2);//10
}
}
final修饰变量的初始化时机:在构造方法完毕前。
那么赋值一般什么时候给呢:
1.定义的时候。(推荐)
2.构造方法中。
2:多态(掌握)
(1)同一个对象在不同时刻体现出来的不同状态。
(2)多态的前提:
A:有继承或者实现关系。
B:有方法重写。
C:有父类或者父接口引用指向子类对象。
父 f = new 子();
多态的分类:
a:具体类多态
class Fu {}
class Zi extends Fu {}
Fu f = new Zi();
b:抽象类多态
abstract class Fu {}
class Zi extends Fu {}
Fu f = new Zi();
c:接口多态
interface Fu {}
class Zi implements Fu {}
Fu f = new Zi();
(3)多态中的成员访问特点
A:成员变量
编译看左边,运行看左边
B:构造方法
子类的构造都会默认访问父类构造
创建子类对象的时候,访问父类的构造方法,对父类的数据进行初始化。
C:成员方法
编译看左边,运行看右边
D:静态方法
编译看左边,运行看左边
(静态和类相关,算不上重写,所以,运行访问的还是左边的)
(运行)只有成员方法是特殊的,为什么?
因为成员方法存在方法重写。
举例子:
a. 成员变量
class Fu {
public int num = 100;
public void show(){
System.out.println("show Fu");
}
}
class Zi extends Fu {
public int num = 1000;
public int num2 = 200;
public void show(){
System.out.println("show Zi");
}
}
class DuoTaiDemo {
public static void main(String[] args){
Fu f = new Zi();
System.out.println(f.num);
//编译看左边,左边Fu类型没有num2
//System.out.println(f.num2);//error.找不到符号
}
}
c. 成员方法
举例子:
class Fu {
public void show(){
System.out.println("show Fu");
}
}
class Zi extends Fu {
public void show(){
System.out.println("show Zi");
}
public void method(){
System.out.println("method zi");
}
}
class DuoTaiDemo {
public static void main(String[] args){
Fu f = new Zi();
//编译看左边,(重写)运行看右边
f.show();
//f.method();//编译看左边,左边父类方法找不到符号
}
}
d. 静态方法
举例子:
class Fu {
public static void function(){
System.out.println("function Fu");
}
}
class Zi extends Fu {
public static void function(){
System.out.println("function Zi");
}
}
class DuoTaiDemo {
public static void main(String[] args){
Fu f = new Zi();
f.function();//function Fu
}
}
(4)多态的好处:
A:提高代码的维护性(继承体现)
B:提高代码的扩展性(多态体现)
(5)多态的弊端:
父不能使用子的特有功能。
举例子:
class Fu{
public void show(){
System.out.println("show fu");
}
}
class Zi extends Fu{
public void show(){
System.out.println("show zi");
}
public void method(){
System.out.println("method zi");
}
}
class DuoTaiDemo3{
public static void main(String[] args){
Fu f = new Zi();
f.show();
//f.method();//错误,不能使用子类的特有功能
}
}
现象:
子可以当作父使用,父不能当作子使用。
(6)多态中的转型问题
A:向上转型 父类引用指向子类对象
从子到父 Fu f = new Zi();
B:向下转型 父类引用强制转换为子类对象。
从父到子 Zi z = (Zi) f;//要求该f必须是能够转换为Zi的。
举例子:
class Fu{
public void show(){
System.out.println("show fu");
}
}
class Zi extends Fu{
public void show(){
System.out.println("show zi");
}
public void method(){
System.out.println("method zi");
}
}
class DuoTaiDemo3{
public static void main(String[] args){
Fu f = new Zi();
f.show();
//f.method();//不能使用子类的特有功能
//你能够把子的对象赋值给父亲,那么我能不能把父的引用赋值给子的引用呢?
//把父类的引用强制转换为子类的引用。
Zi z = (Zi)f;
z.show();
z.method();
}
}
(7)多态的练习
A:猫狗案例
举例子:
class Animal {
public void eat(){
System.out.println("吃饭");
}
}
class Dog extends Animal {
public void eat(){
System.out.println("狗吃肉");
}
public void lookDoor(){
System.out.println("狗看门");
}
}
class Cat extends Animal {
public void eat(){
System.out.println("猫吃鱼");
}
public void playGame(){
System.out.println("猫捉迷藏");
}
}
class DuoTaiTest {
public static void main(String[] args){
//定义为狗
Animal a = new Dog();
a.eat();
System.out.println("--------");
//还原成狗
Dog d = (Dog) a;
d.eat();
d.lookDoor();
System.out.println("--------");
//变成猫
a = new Cat();
a.eat();
System.out.println("-------");
//还原成猫
Cat c = (Cat) a;
c.eat();
c.playGame();
}
}
B:北方人和南方人案例
/*
不同地方饮食文化不同的案例
*/
class Person {
public void eat(){
System.out.println("吃饭");
}
}
class SouthPerson extends Person {
public void eat(){
System.out.println("炒菜,吃米饭");
}
public void jingShang(){
System.out.println("经商");
}
}
class NorthPerson extends Person {
public void eat(){
System.out.println("炖菜,吃面条");
}
public void yanJiu(){
System.out.println("学术");
}
}
class DuoTaiTest2 {
public static void main(String[] args){
//测试
//南方人
Person p = new SouthPerson();
p.eat();
System.out.println("-------");
SouthPerson sp = (SouthPerson) p;
sp.eat();
sp.jingShang();
System.out.println("-------");
//北方人
p = new NorthPerson();
p.eat();
System.out.println("-------");
NorthPerson np = (NorthPerson) p;
np.eat();
np.yanJiu();
}
}
3:抽象类与多态(掌握)
把多个共性的东西(方法)提取到一个类中,这是继承的做法。
但是呢,这多个共性的东西,在有些时候,方法声明一样,但是方法体在具体实现的时候内容不一样。
所以,我们在定义这些共性的方法的时候,就不能给出具体的方法体, 而一个没有具体的方法体的方法是抽象的方法。
(1) 抽象类的概述:
动物不应该定义为具体的东西,而且动物中的吃,睡等也不应该是具体的。
我们把一个不是具体的功能称为抽象的功能,如果一个类中有抽象的功能(方法),该类必须是抽象类。
(2) 格式格式:
abstract class 类名{}
public abstract void eat();
(3)抽象类的特点
A:抽象类和抽象方法必须用abstract关键字修饰
B:抽象类中不一定有抽象方法,但是有抽象方法的类必须定义为抽象类。
C:抽象类不能实例化
因为它不是具体的。
抽象类是有构造方法的,但是不能实例化。那么,构造方法的作用是什么?
用于子类访问父类数据的构造化
D:抽象类的子类
1.是一个抽象类:如果不想重写抽象方法,该子类是一个抽象类。
2.是一个具体类:这个类必须重写抽象类中所有的抽象方法。
E:抽象类的实例化其实是以多态的方式,靠具体的子类实现的。
Animal a = new Cat();
例子:
//abstract class Animal ,抽象类的声明格式
abstract class Animal{
//public abstract void eat(){} //注意区分抽象方法(不能有主体)和空方法体
public abstract void eat();//抽象方法
public Animal(){}//抽象类是有构造方法的
}
//子类是抽象类
abstract class Dog extends Animal {}
//子类是具体类,重写抽象方法
class Cat extends Animal {
public void eat(){
System.out.println("猫吃鱼");
}
}
class AbstractDemo{
public static void main(String[] args){
//创建对象
//Animal是抽象的;无法实例化
//Animal a = new Animal();
//抽象类可以通过多态(子类)的方式实例化
Animal a = new Cat();
a.eat();
}
}
抽象类是不能通过构造方法实例化的,那么抽象类怎么实例化呢?
抽象类可以通过多态(子类)的方式实例化。
多态最主要的利用,不是在具体类之间使用。而是在抽象类之间使用!抽象类向外界提供的是一个个抽象的东西,而它的子类却是靠一个个具体的方法实现的(抽象类的子类必须重写抽象类中所有的抽象方法。)
(4)抽象类的成员特点:
A:成员变量
有变量,有常量
B:构造方法
有构造方法 。用于子类访问父类数据的初始化。
C:成员方法
既可以有抽象方法,也可以有非抽象方法。
抽象类的成员方法特性:
A:抽象方法,强制要求子类做的事情。
B:非抽象方法,子类继承的事情,提高代码复用性。
成员变量举例子:
abstract class Animal {
//抽象类里面的成员变量,可以有常量有变量
public int num = 10;//变量
public final int num2 = 20;//常量
}
class Dog extends Animal {
}
class AbstractDemo2 {
public static void main(String[] args){
//创建对象
Animal a = new Dog();
System.out.println(a.num);//10
a.num = 100;
System.out.println(a.num);//100
System.out.println(a.num2);
//a.num2 = 200;//错误: 无法为最终变量num2分配值
System.out.println(a.num2);
}
}
构造方法举例子:
abstract class Animal {
public int num = 10;//变量
public final int num2 = 20;//常量
public Animal(){}//无参构造方法,用于子类访问父类数据的初始化。
public Animal(String name,int age){} //有参构造方法,用于子类访问父类数据的初始化。
}
class Dog extends Animal {
}
class AbstractDemo2 {
public static void main(String[] args){
Animal a = new Dog();
System.out.println(a.num);
System.out.println(a.num2);
}
}
成员方法举例子:
abstract class Animal {
//既可以有抽象方法,也可以有非抽象方法。
public abstract void show();
public void method(){
System.out.println("method");
}
}
class Dog extends Animal {
public void show(){
System.out.println("show Dog");
}
}
class AbstractDemo2 {
public static void main(String[] args){
//创建对象
Animal a = new Dog();
a.show();
a.method();
}
}
(5)抽象类的练习——猫狗案例练习
猫狗案例
具体事物:猫,狗
共性:姓名,年龄,吃饭
分析:从具体到抽象
猫:
成员变量:姓名,年龄
构造方法:无参,带参
成员方法:吃饭(猫吃鱼)
狗:
成员变量:姓名,年龄。
构造方法:无参,带参。
成员方法:吃饭(狗吃肉)
因为有共性的内容,所以就提取了一个父类。动物。
但是又由于吃饭的内容不一样,所以吃饭的方法是抽象的。
而方法是抽象的类,类就必须定义为抽象类。
抽象动物类:
成员变量:姓名,年龄。
构造方法:无参,带参。
成员方法:吃饭();
实现:从抽象到具体
动物类:
成员变量:姓名,年龄。
构造方法:无参,带参。
成员方法:吃饭();
狗类:
继承自动物类
重写吃饭();
猫类:
继承自动物类
重写吃饭();
abstract class Animal{
//姓名
private String name;
//年龄
private int age;
public Animal(){}
public Animal(String name ,int age){
this.name = name;
this.age = age;
}
public String getName(){
return name;
}
public void setName(String name){
this.name = name;
}
public int getAge(){
return age;
}
public void setAge(int age){
this.age = age;
}
//定义一个抽象方法
public abstract void eat();
}
//定义具体的狗类
class Dog extends Animal{
public Dog(){}
public Dog(String name,int age){
super(name,age);
}
public void eat(){
System.out.println("狗吃肉");
}
}
//定义具体的猫类
class Cat extends Animal{
public Cat(){}
public Cat(String name,int age){
super(name,age);
}
public void eat(){
System.out.println("猫吃鱼");
}
}
//测试类
class AbstractTest {
public static void main(String[] args){
//测试狗类
//具体类用法
//方式1:
Dog d = new Dog();
d.setName("旺财");
d.setAge(3);
System.out.println(d.getName()+"---"+d.getAge());
d.eat();
//方式2:
Dog d2 = new Dog("旺财",3);
System.out.println(d2.getName()+"----"+d.getAge());
d2.eat();
System.out.println("------------------------------");
Animal a = new Dog();
a.setName("旺财");
a.setAge(3);
System.out.println(a.getName()+"----"+a.getAge());
a.eat();
Animal a2 = new Dog("旺财",3);
System.out.println(a.getName()+"----"+a.getAge());
a2.eat();
}
}
(6)抽象类的几个小问题
A:抽象类有构造方法,不能实例化,那么构造方法有什么用?
用于子类访问父类数据的初始化
B:一个类如果没有抽象方法,却定义为了抽象类,有什么用?
为了不让外界创建对象
C:abstract不能和哪些关键字共存
a:final 冲突
b:private 冲突
c:static 无意义
举例子:
- abstract和private冲突
abstract class Fu {
//非法的修饰符组合: abstract和private
//private只能被Fu类访问,private不能被继承就不能被子类重写,抽象方法要求被重写呢?
private abstract void show();
}
class Zi extends Fu {
public void show() {}
}
class AbstractDemo3 {
public static void main(String[] args) {
}
}
2. abstract和final冲突
同理
abstract class Fu {
//错误: 非法的修饰符组合: abstract和final
//final修饰的方法不能被子类重写
final abstract void show();
}
class Zi extends Fu {
public void show() {}
}
class AbstractDemo3 {
public static void main(String[] args) {
}
}
3.static和abstract无意义
abstract class Fu {
//错误: 非法的修饰符组合: abstract和static
//static abstract void show();
//静态方法可以通过类名.方法名()的格式访问,而抽象方法恰好是没有方法体的
//访问一个没有方法体的方法,无意义
public static void method(){
System.out.println("method");
}
}
class Zi extends Fu {
public void show() {}
}
class AbstractDemo3 {
public static void main(String[] args) {
//静态方法可以通过类名.方法名()的格式访问
Fu.method();
}
}
4:接口与多态(掌握)
(1)回顾猫狗案例,它们仅仅提供一些基本功能。
比如:猫钻火圈,狗跳高等功能,不是动物本身就具备的,
是在后面的培养中训练出来的,这种额外的功能定义到动物类中不适合,也不适合直接定义到猫、狗中。事物功能的扩展性,java提供了接口来定义这些额外功能。
(2)接口的特点:
A:接口用关键字interface表示
interface 接口名{}
接口并不是实际意义上的一个类,仅仅表示功能的扩展。
B:类实现接口用implements表示
class 类名 implements 接口名{}
C:接口不能实例化
那么,接口如何实例化呢?
按照多态的方式,由具体的子类来实例化。其实这也是多态的一种,接口多态。
由此可见,多态:
A:具体类多态(几乎没有)
B:抽象类多态(常用)
C:接口多态(最常用)
D:接口的子类
要么是抽象类,但是意义不大。
要么(具体类)重写接口中的所有抽象方法(推荐方案)。
举例子:
//定义动物培训接口
interface AnimalTrain{
public abstract void jump();
}
//抽象类实现接口
abstract class Dog implements AnimalTrain {
}
//具体类实现接口
class Cat implements AnimalTrain {
public void jump(){
System.out.println("猫可以跳高了");
}
}
class InterfaceDemo {
public static void main(String[] args){
//AnimalTrain是抽象的;无法实例化
//AnimalTrain at = new AnimalTrain();
//at.jump();
AnimalTrain at = new Cat();
at.jump();
}
}
(3) 接口的成员特点:
A.成员变量:只能是常量。默认修饰符public static final
B.构造方法:没有。因为接口主要是扩展功能的,而没有具体存在。
C.成员方法:只能是抽象方法,默认修饰符public abstract
举例子:
A.成员变量
接口中的变量默认只能是常量,并且是静态的。
默认修饰符:public static final
建议:自己手动给出public static final。
interface Inter {
public int num = 10;
public final int num2 = 20;
public static final int num3 = 30;//建议自己手动给出
}
//“接口名+Impl”这种格式是接口的实现类格式
class InterImp1 implements Inter {
}
class InterfaceDemo2{
public static void main(String[] args){
//创建对象
Inter i = new InterImp1();
System.out.println(i.num);
System.out.println(i.num2);
i.num = 100;// 错误: 无法为最终变量num分配值
i.num2 = 200;// 错误: 无法为最终变量num2分配值
System.out.println(i.num);
System.out.println(i.num2);
System.out.println("-----------");
System.out.println(Inter.num);
System.out.println(Inter.num2);
}
}
B:构造方法
接口是没有构造方法的。
所有的类都默认继承自一个类:Object。类 Object 是类层次结构的根类。每个类都使用 Object 作为超类。
举例子:
interface Inter {
public static final int num3 = 30;
}
//“接口名+Impl”这种格式是接口的实现类格式
class InterImp1 implements Inter {
public InterImp1(){
super();//疑问?
}
}
疑问:如果接口没有构造方法,那么这里super()调用父类的无参构造方法怎么解释?
回答:所有的类都默认继承自一个类:Object。
所以,接口没有构造方法,不会影响子类访问父类super();
class InterImp1 extends Object implements Inter {
public InterImp1(){
super();
}
}
class InterfaceDemo2{
public static void main(String[] args){
//创建对象
Inter i = new InterImp1();
System.out.println(Inter.num3);
}
}
C:成员方法
接口中的方法只能是抽象方法(接口方法不能带有主体)。
默认修饰符:public abstract
建议:自己手动给出!
(4)类与类,类与接口,接口与接口
A:类与类
继承关系,只能单继承,可以多层继承
B:类与接口
实现关系,可以单实现,也可以多实现。
还可以在继承一个类的同时,实现多个接口
举例子:
interface Father {
public abstract void show();
}
interface Mother {
public abstract void show2();
}
class Son extends Object implements Father,Mother{
public void show(){
System.out.println("show son");
}
public void show2(){
System.out.println("show2 son");
}
}
class InterfaceDemo3 {
public static void main(String[] args){
//创建对象
//实现了多个接口,记住对应的接口调用对应的方法
Father f = new Son();
f.show();
//f.show2();//报错
Mother m = new Son();
//m.show();//报错
m.show2();
}
}
C:接口与接口
继承关系,可以单继承,也可以多继承
举例子:
interface Father {
public abstract void show();
}
interface Mother {
public abstract void show2();
}
interface Sister extends Father,Mother{
}
(5)抽象类和接口的区别(开发中意义不大,面试居多)?
A:成员区别
抽象类:
成员变量:可以变量,也可以常量。
构造方法:有。
成员方法:可以抽象,也可以非抽象。
接口:
成员变量:只可以常量。
构造方法:无。
成员方法:只可以抽象。
B:关系区别:
类与类
继承关系,只能单继承,可以多层继承
类与接口
实现关系,可以单实现,也可以多实现。
还可以在继承一个类的同时,实现多个接口
接口与接口
继承关系,可以单继承,也可以多继承
C:设计理念不同
抽象类:被继承体现的是“is a”的关系,抽象类中定义的是该继承体系的共性功能。
接口:被实现体现的是“like a”的关系,接口中定义的是该继承体系的扩展功能(类比 usb接口)。
(6)3个案例:
猫狗案例、老师和学生案例、教练和运动员案例
A:猫狗案例,加入跳高功能
猫狗案例,加入跳高的额外功能。
分析:从具体到抽象。
猫:
姓名,年龄
吃饭,睡觉
狗:
姓名,年龄
吃饭,睡觉
由于有共性功能,所以,我们抽取出一个父类。
动物:
姓名,年龄
吃饭();//抽象方法
睡觉(){}//非抽象方法
猫:继承自动物
狗:继承自动物
调高的功能是一个新的扩展功能,所以我们要定义一个接口。
接口:
跳高。
部分猫:实现跳高。
部分狗:实现跳高。
实现:
从抽象到具体。
使用:
使用具体类
//定义跳高接口
interface Jumpping {
//跳高功能
public abstract void jump();
}
//定义抽象类
abstract class Animal {
//姓名
private String name;
//年龄
private int age;
public Animal(){}
public Animal(String name,int age){
this.name = name;
this.age = age;
}
public String getName(){
return name;
}
public void setName(String name){
this.name = name;
}
public int getAge(){
return age;
}
public void setAge(int age){
this.age = age;
}
//吃饭();
public abstract void eat();
//睡觉();
public void sleep(){
System.out.println("睡觉觉了");
}
}
// 具体猫类
class Cat extends Animal {
public Cat(){
}
public Cat(String name,int age){
super(name,age);
}
public void eat(){
System.out.println("猫吃鱼");
}
}
// 具体狗类
class Dog extends Animal {
public Dog(){
}
public Dog(String name,int age){
super(name,age);
}
public void eat(){
System.out.println("狗吃肉");
}
}
// 有跳高功能的猫
class JumpCat extends Cat implements Jumpping{
public JumpCat(){}
public JumpCat(String name,int age){
super(name,age);
}
public void jump(){
System.out.println("跳高猫");
}
}
// 有跳高功能的狗
class JumpDog extends Dog implements Jumpping{
public JumpDog(){}
public JumpDog(String name,int age){
super(name,age);
}
public void jump(){
System.out.println("跳高狗");
}
}
//定义跳高接口
class InterfaceTest {
public static void main(String[] args){
//定义跳高猫
JumpCat jc = new JumpCat();
jc.setName("哆啦A梦");
jc.setAge(3);
System.out.println(jc.getName()+"-----"+jc.getAge());
jc.eat();
jc.sleep();
jc.jump();
System.out.println("--------------");
JumpCat jc2 = new JumpCat("加菲猫",2);
System.out.println(jc2.getName()+"-----"+jc2.getAge());
jc2.eat();
jc2.sleep();
jc2.jump();
}
}
B:老师和学生案例,加入抽烟功能
老师和学生案例,加入抽烟的额外功能
分析:从具体到抽象
老师:姓名,年龄,吃饭,睡觉
学生:姓名,年龄,吃饭,睡觉
由于有共性功能,我们提取出一个父类,人类。
人类:
姓名,年龄
吃饭();//抽象,空语句
睡觉(){}//具体
抽烟的额外功能不是人或者老师,或者学生一开始就应该具备的。所以,我们把它定义为接口
抽烟接口。
部分老师抽烟:实现抽烟接口。
部分学生抽烟:实现抽烟接口。
实现:从抽象到具体。
使用:具体。
//定义抽烟接口
interface Smoking {
//抽烟的抽象方法
public abstract void smoke();
}
//定义抽象人类
abstract class Person {
//姓名
private String name;
//年龄
private int age;
public Person(){}
public Person(String name,int age){
this.name = name;
this.age = age;
}
public String getName(){
return name;
}
public void setName(String name){
this.name = name;
}
public int getAge(){
return age;
}
public void setAge(int age){
this.age = age;
}
//吃饭();
public abstract void eat();
//睡觉(){}
public void sleep() {
System.out.println("睡觉觉了");
}
}
//具体老师类
class Teacher extends Person {
public Teacher(){}
public Teacher(String name,int age){
super(name,age);
}
public void eat(){
System.out.println("开水煮白菜");
}
}
//具体学生类
class Student extends Person {
public Student(){}
public Student(String name,int age){
super(name,age);
}
public void eat(){
System.out.println("吃红烧肉");
}
}
//抽烟的老师
class SmokingTeacher extends Teacher implements Smoking{
public SmokingTeacher(){}
public SmokingTeacher(String name,int age){
super(name,age);
}
public void smoke(){
System.out.println("抽烟的老师");
};
}
//抽烟的学生
class SmokingStudent extends Student implements Smoking{
public SmokingStudent(){}
public SmokingStudent(String name,int age){
super(name,age);
}
public void smoke(){
System.out.println("抽烟的学生");
};
}
class InterfaceTest2 {
public static void main(String[] args){
//测试学生
SmokingStudent ss = new SmokingStudent();
ss.setName("刘德华");
ss.setAge(27);
System.out.println(ss.getName()+"----"+ss.getAge());
ss.eat();
ss.sleep();
ss.smoke();
System.out.println("-------------");
SmokingStudent ss2 = new SmokingStudent("梁朝伟",32);
System.out.println(ss2.getName()+"----"+ss2.getAge());
ss2.eat();
ss2.sleep();
ss2.smoke();
}
}
C教练和运动员案例(类、抽象类、接口的综合小案例)
2018年7月1日10:52:57
教练和运动员案例(学生分析然后讲解)
乒乓球运动员和篮球运动员
乒乓球教练和篮球教练
为了出国交流,跟乒乓球相关的人员都需要学习英语
请运用所学的知识:
分析,这个案例中有哪些抽象类,哪些接口,哪些具体类
整个分析过程,我是通过画图讲解的。
//定义一个说英语的接口
interface SpeakEnglish {
//说英语
public abstract void speak();
}
//定义人的抽象类
abstract class Person {
private String name;
private int age;
public Person(){}
public Person(String name,int age){
this.name = name;
this.age = age;
}
public String getName(){
return name;
}
public void setName(String name){
this.name = name;
}
public int getAge(){
return age;
}
public void setAge(int age){
this.age = age;
}
//睡觉
public void sleep(){
System.out.println("人都是要睡觉的");
}
//吃饭
public abstract void eat();
}
//定义运动员抽象类
abstract class Player extends Person {
public Player(){}
public Player(String name,int age){
super(name,age);
}
//学习
public abstract void study();
}
//定义教练抽象类
abstract class Coach extends Person {
public Coach(){}
public Coach(String name,int age){
super(name,age);
}
//教
public abstract void teach();
}
//定义乒乓球运动员具体类
class PingPangPlayer extends Player implements SpeakEnglish {
public PingPangPlayer(){}
public PingPangPlayer(String name,int age){
super(name,age);
}
//吃
public void eat(){
System.out.println("乒乓球运动员吃大白菜,喝小米粥");
}
//学习
public void study(){
System.out.println("乒乓球运动员学习如何发球和接球");
}
//说英语
public void speak(){
System.out.println("乒乓球运动员说英语");
}
}
//定义篮球运动员具体类
class BasketballPlayer extends Player {
public BasketballPlayer(){}
public BasketballPlayer(String name,int age){
super(name,age);
}
//吃
public void eat(){
System.out.println("篮球运动员吃牛肉,喝牛奶");
}
//学习
public void study(){
System.out.println("篮球运动员学习如何运球和投篮");
}
}
//定义乒乓球教练具体类
class PingPangCoach extends Coach implements SpeakEnglish {
public PingPangCoach(){}
public PingPangCoach(String name,int age){
super(name,age);
}
//吃
public void eat(){
System.out.println("乒乓球教练吃小白菜,喝大米粥");
}
//教
public void teach(){
System.out.println("乒乓球教练教如何发球和接球");
}
//说英语
public void speak(){
System.out.println("乒乓球教练说英语");
}
}
//定义篮球教练具体类
class BasketballCoach extends Coach {
public BasketballCoach(){}
public BasketballCoach(String name,int age){
super(name,age);
}
//吃
public void eat(){
System.out.println("篮球教练吃羊肉,喝羊奶");
}
//教
public void teach(){
System.out.println("篮球教练教如何运球和投篮");
}
}
class InterfaceDemo {
public static void main(String[] args){
//测试运动员(乒乓球运动员和篮球运动员)
PingPangPlayer ppp = new PingPangPlayer();
ppp.setName("王浩");
ppp.setAge(33);
System.out.println(ppp.getName()+"----"+ppp.getAge());
ppp.eat();
ppp.study();
ppp.speak();
ppp.sleep();
//通过带参构造给数据
PingPangPlayer ppp2 = new PingPangPlayer("王浩",33);
System.out.println(ppp2.getName()+"----"+ppp2.getAge());
ppp2.eat();
ppp2.study();
ppp2.speak();
ppp2.sleep();
//篮球运动员
BasketballPlayer bp = new BasketballPlayer();
bp.setName("姚明");
bp.setAge(34);
System.out.println(bp.getName()+"----"+bp.getAge());
bp.eat();
bp.study();
//bp.speak();//没有该方法
bp.sleep();
//通过带参构造给数据
BasketballPlayer bp2 = new BasketballPlayer("姚明",34);
System.out.println(bp2.getName()+"----"+bp2.getAge());
bp2.eat();
bp2.study();
//bp2.speak();//没有该方法
bp2.sleep();
}
}
5:形式参数和返回值的问题(理解)
(1)形式参数:
基本类型(太简单,不是我今天要讲解的)
引用类型
类名:(匿名对象的时候其实我们已经讲过了)需要的是该类的对象。
抽象类:需要的是该抽象类的子类对象。
接口:需要的是该接口的实现类对象。
a.类名:需要该类的对象
class Student {
public void study(){
System.out.println("Good Good Study,Day Day Up");
}
}
class StudentDemo {
public void method(Student s){//将ss赋给s,ss = new
// Student(); Student s = new Student();
s.study();
}
}
class StudentTest {
public static void main(String[] args){
//需求:我要测试学生类的study方法
Student s = new Student();
s.study();
System.out.println("-----------");
//需求2:我要测试StudentDemo类中的method()方法
StudentDemo sd = new StudentDemo();
Student ss = new Student();
sd.method(ss);
// 匿名对象用法
new StudentDemo().method(new Student());
}
}
b.抽象类名:需要的是该抽象类的子类对象
举例子:
abstract class Person {
public abstract void study();
}
class PersonDemo {
//抽象类调用方法,肯定是用子类去实现的
public void method(Person p){//p; p = new Student(); Person p = new Student();//多态
p.study();
}
}
//定义一个具体的学生类
class Student extends Person {
public void study(){
System.out.println("Good Good Study,Day Day Up");
}
}
class PersonTest {
public static void main(String[] args){
//目前是没有办法使用的
//因为抽象类没有对应的具体类
//那么,我们就应该先定义一个具体类
//需求:我要使用PersonDemo类中的method()方法
PersonDemo pd = new PersonDemo();
Person p = new Student();
pd.method(p);
}
}
c.接口名:需要该接口的实现类对象
举例子:
//定义一个爱好的接口
interface Love {
public abstract void love();
}
class LoveDemo {
public void method(Love l){//l; l = new Teacher(); Love l = new Teacher(); 多态
l.love();
}
}
//定义具体类实现接口
class Teacher implements Love {
public void love(){
System.out.println("老师爱学生,爱java,爱林青霞");
}
}
class TeacherTest {
public static void main(String[] args){
//需求:我要测试LoveDemo类中的love()方法
LoveDemo ld = new LoveDemo();
Love l = new Teacher();
ld.method(l);
}
}
(2)返回值类型:
基本类型:(基本类型太简单,不准备讲解)
引用类型:
类:返回的是该类的对象。
抽象类:返回的是该抽象类的子类对象。
接口:返回的是该接口的实现类的对象
a.类名:返回的是该类的对象
举例子:返回值类型是一个类名,真正返回的是该类的对象。
class Student {
public void study(){
System.out.println("Good Good Study,Day Day Up");
}
}
class StudentDemo {
public Student getStudent(){
//Student s = new Student();
//Student ss = s;
//Student s = new Student();
//return s;
return new Student();
}
}
class StudentTest2 {
public static void main(String[] args){
//需求:我要使用Student类中的study()方法
//但是,这一次我的要求是,不要直接创建Student的对象
//让你使用StudentDemo帮你创建对象
StudentDemo sd = new StudentDemo();
Student s = sd.getStudent();//new Student();Student s = new Student();
s.study();
}
}
b.抽象类名:返回的是该抽象类的子类对象
举例子:
abstract class Person {
public abstract void study();
}
class PersonDemo {
public Person getPerson(){
//return new Person;//Person是一个抽象类
return new Student();
}
}
class Student extends Person {
public void study(){
System.out.println("Good Good Study,Day Day Up");
}
}
class PersonTest2 {
public static void main(String[] args){
//需求:我要测试Person类中的study()方向
PersonDemo pd = new PersonDemo();
Person p = pd.getPerson();//Person p = new Student();多态
p.study();
}
}
c.接口名:返回的是该接口的实现类的对象
举例子:
//定义一个爱好的接口
interface Love {
public abstract void love();
}
class LoveDemo {
public Love getLove(){
//Love l = new Teacher();
//return l;
return new Teacher();
}
}
//定义具体类实现接口
class Teacher implements Love {
public void love(){
System.out.println("老师爱学生,爱java,爱林青霞");
}
}
class TeacherTest2 {
public static void main(String[] args){
//如何测试呢?
LoveDemo ld = new LoveDemo();
Love l = ld.getLove();// Love l = new Teacher();多态
l.love();
}
}
(3)链式编程
对象.方法1().方法2().......方法n();
每次调用完方法后,返回的是一个对象。
这种用法:其实在方法1()调用完毕后,应该一个对象;
方法2()调用完毕后,应该返回一个对象。
方法n()调用完毕后,可能是对象,也可以不是对象。
举例子:
class Student {
public void study(){
System.out.println("Good Good Study,Day Day Up");
}
}
class StudentDemo {
public Student getStudent(){
return new Student();
}
}
class StudentTest3 {
public static void main(String[] args){
//如何调用的呢?
StudentDemo sd = new StudentDemo();
//Student s = sd.getStudent();
//s.study();
//大家注意了,链式编程
sd.getStudent().study();
}
}
6:包(理解)
(1):其实就是文件夹
(2):作用
A:把相同的类名放到不同的包中(区分同名的类)
B:对类进行分类管理(重点)
a:按照功能分
b:按照模块分
举例:
老师类:增加、删除、修改、查询
学生类:增加、删除、修改、查询
......
方案1:按照功能分
cn.itcast.add
AddStudent
AddTeacher
cn.itcast.delete
DeleteStudent
DeleteTeacher
cn.itcast.update
UpdateStudent
UpdateTeacher
cn.itcast.find
FindStudent
FindTeacher
方案2:按照模块分
cn.itcast.teacher
AddTeacher
DeleteTeacher
UpdateTeacher
FindTeacher
cn.itcast.student
AddStudent
DeleteStudent
UpdateStudent
FindStudent
(3)包的定义(掌握):
Package 包名;
多级包用.分开即可
(4)注意事项(掌握):
A:package语句必须是程序的第一条可执行的代码
B:package语句在一个java文件中只能有一个
C:如果没有package,默认表示无包名。
带包的编译和运行:
A: 手动式
a:编写一个带包的java文件(HelloWorld.java)
package cn.itcast;
class HelloWorld {
public static void main(String[] args){
System.out.println("HelloWorld");
}
}
b:通过javac命令编译该java文件
c:手动创建包名。
cn/itcast
c:把b步骤的class文件放到(剪切)c步骤的最底层包。
e:回到和包根目录在同一目录的地方,然后带包运行。
java cn.itcast.HelloWorld
B:自动式(掌握)
a:编写一个带包的java文件。
b:javac编译的时候带上-d即可
java –d . HelloWorld.java
C:回到和包根目录在同一目录的地方,然后带包运行。
java cn.itcast.HelloWorld
7:导包(掌握)
(1)不同包下的类之间的访问,我们发现,每次使用不同包下的类的时候,都需要加包的全路径。比较麻烦。这个时候,java就提供了导包的功能。
(1) 导包
格式:import 包名...类名; 这种方式是导入到类的级别。
另一种:
import 包名...*;(不建议)
注意:用到哪个类就导哪个类
(3)package,import,class的顺序关系
package > import > class(package在最前面)
Package:只能有一个
import:可以有多个
class:可以有多个,以后建议是一个。
8:权限修饰符(掌握)
(1)权限修饰符
| 本类 | 同一个包下(子类和无关类) | 不同包下的子类 | 不同包下的无关类 |
private | Y |
|
|
|
默认 | Y | Y |
|
|
protected | Y | Y | Y |
|
public | Y | Y | Y | Y |
(2)这四种权限修饰符在任意时刻只能出现一种。
public class Demo {}
(3):常见的修饰符(理解)
(1)分类:
权限修饰符:private,默认修饰符,protected,public
状态修饰符:static,final
抽象修饰符:abstract
(2)常见修饰符的使用(不用记)
类:
权限修饰符:默认修饰符,public
状态修饰符:final
抽象修饰符:abstract
用的最多的就是:public
成员变量:
权限修饰符:private,默认的,protected,public
权限修饰符:static,final
用的最多的就是:private
构造方法:
权限修饰符:private,默认的,protected,public
构造方法只能使用权限修饰符修饰
用的最多的就是:public
成员方法:
权限修饰符:private,默认的,protected,public
状态修饰符:static,final
抽象修饰符:abstract
用的最多的就是:public
要会自己测试!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | public class Demo { //成员变量 private int x = 10; int y = 20; protected int z = 30; public int a = 40; public final int b = 50; public final int c = 60; public static final int d = 70; //错误: 此处不允许使用修饰符abstract //abstract int e = 80;
//构造方法 private Demo(){}
Demo(String name){}
protected Demo(String name,int age){}
public Demo(String name,int age,String address){}
//构造方法只能使用权限修饰符修饰:private,默认的,protected,public
//错误: 此处不允许使用修饰符static //public static Demo(){} //错误: 此处不允许使用修饰符final //public final Demo(){} //错误: 此处不允许使用修饰符abstract //public abstract Demo(){}
//成员方法 //static void show(){} //abstract void show(); //final void show(){}
} |
(3)另外比较常见的:
成员变量:public static final
public static final int X = 10;
成员方法:public static
public abstract
public final
public static void show() {}
public final void show() {}
public abstract void show();
9:内部类(理解)
(1)内部类概述:
把类定义在其他类的内部,这个类就被称为内部类。
举例:把类B定义在类A中,类B就被称为内部类。
(2)内部类的访问规则
A:可以直接访问外部类的成员,包括私有
B:外部类要想访问内部类成员,必须创建对象
举例子:
class Outer {
private int num = 10;
class Inner {
public void show(){
System.out.println(num);
}
}
public void method(){
//找不到符号
//show();
Inner i = new Inner();
i.show();
}
}
class InnerClassDemo {
public static void main(String[] args){
}
}
(3)内部类的分类
内部类分为成员内部类和局部内部类
根据内部类的位置
成员位置:在成员位置定义的类,被称为成员内部类。
局部位置:在局部位置定义的类,被称为局部内部类。
举例子:
class Outer {
private int num = 10;
//成员位置
class Inner {
}
public void method(){
//局部位置
class Inner {
}
}
}
class InnerClassDemo2 {
public static void main(String[] args){
}
}
(4)成员内部类的调用
成员内部类:在成员位置定义的类,被称为成员内部类。
如何直接访问成员内部类的成员。
格式:外部类名.内部类名 对象名 = 外部类对象.内部类对象;
举例子:
class Outer {
private int num = 10;
class Inner {
public void show(){
System.out.println(num);
}
}
}
class InnerClassDemo3 {
public static void main(String[] args){
//需求:我要访问Inner类的show()方法
//Inner i = new Inner();
//i.show();
//格式:外部类名.内部类名 对象名 = 外部类对象.内部类对象;
Outer.Inner oi = new Outer().new Inner();
oi.show();
}
}
(5)成员内部类的常见修饰符
A:private 为了保证数据的安全性
B:static 为了访问的方便性
注意:静态内部类访问的外部类数据,外部类数据必须用静态类修饰
也就是说被静态修饰的成员内部类只能访问外部类的静态成员
案例:关于private,我有一个人(人有身体,身体内有心脏)
class Body{
class Heart{
public void operator(){
System.out.println("心脏搭桥");
}
}
}
按照我们刚才的讲解,来使用一下
Body.Heart bh = new Body().new Heart();
bh.operator();
在实际开发中,一般内部类是不让外界直接访问的。
//加了private后,就不能被外界访问了,那么,怎么玩呢?
由外部类Body提供一个方法method()
class Body{
private class Heart{
public void operator(){
System.out.println("心脏搭桥");
}
}
public void method(){
if(如果你是外科医生){
Heart h = new Heart();
h.operator();
}
}
}
Body b = new Body();
b.method();
B:static 为了访问的方便性
成员内部类不是静态的:
外部类名.内部类名 对象名 = new 外部类名.new 内部类名();
成员内部类是静态的:
外部类名.内部类名 对象名 = new 外部类名.内部类名();
举例子:
class Outer {
private int num = 10;
private static int num2 = 100;
//静态不能修饰类(外部类)
//内部类用静态修饰是因为内部类可以看成是外部类的成员
public static class Inner {
public void show() {
//System.out.println(num);
//注意:静态内部类访问的外部类数据必须用静态修饰。
System.out.println(num2);
}
public static void show2() {
//System.out.println(num);
System.out.println(num2);
}
}
}
class InnerClassDemo4 {
public static void main(String[] args) {
//使用内部类
//错误: 限定的新静态类
//Outer.Inner oi = new Outer().new Inner();
//oi.show();
//oi.show2();
//成员内部类被静态修饰后的访问方式是:
//格式:外部类名.内部类名 对象名 = new 外部类名.内部类名();
Outer.Inner oi = new Outer.Inner();
oi.show();
oi.show2();
//show2()的另一种调用方式
Outer.Inner.show2();
}
}
(6)成员内部类的面试题
面试题:
补齐代码,在控制台分别输出30,20,10。
class Outer {
public int num = 10;
class Inner {
public int num = 20;
public void show(){
int num = 30;
System.out.println(?);
System.out.println(??);
System.out.println(???);
}
}
}
注意:
1.内部类和外部类没有继承关系。
2.通过外部类名限定this对象——Outer.this
class Outer {
public int num = 10;
class Inner {
public int num =20;
public void show(){
int num = 30;
System.out.println(num);
System.out.println(this.num);
//System.out.println(new Outer().num);
System.out.println(Outer.this.num);// new Outer().num也行
}
}
}
class InnerClassTest {
public static void main(String[] args){
Outer.Inner oi = new Outer().new Inner();
oi.show();
}
}
(7)局部内部类
A:可以直接访问外部类的成员
B:在局部位置,可以创建内部类对象。通过对象调用内部类方法,来使用局部内部类功能。
举例子:
class Outer {
private int num = 10;
public void method(){
class Inner {
public void show(){
//可以直接访问外部类的成员
System.out.println(num);
}
}
//在局部位置,可以创建内部类对象。通过对象调用内部类方法,来使用局部内部类功能。
Inner i = new Inner();
i.show();
}
}
class Test {
public static void main(String[] args){
Outer o = new Outer();
o.method();//10
}
}
局部内部类访问局部变量的注意事项?
A:局部内部类访问局部变量,局部变量必须加final修饰。
B:为什么呢?
因为局部变量使用完毕就消失,而堆内存的数据(对象)并不会立即消失。
所以,堆内存还是用该变量,而改变量已经没有了。
为了让该值还存在,就加final修饰。
通过反编译工具我们看到了,加入final修饰后,这个变量就变成了常量。堆内存直接存储的是数值,而不是变量名。
举例子:
class Outer {
private int num = 10;
public void method(){
final int num2 = 20;//此处必须加final不然会报错
class Inner {
public void show(){
System.out.println(num);
//错误: 从内部类中访问本地变量num2; 需要被声明为最终类型
System.out.println(num2);
}
}
Inner i = new Inner();
i.show();
}
}
class InnerClassDemo {
public static void main(String[] args){
Outer o = new Outer();
o.method();
}
}
(8)匿名内部类(掌握)
A:其实就是局部内部类的简化形式
B:前提
存在一个类或者接口。
这里的类可以是具体类也可以是抽象类。
C:格式:
new 类名或者接口名() {
重写方法;
}
D:本质是什么呢?
其实是一个继承了该类或者实现了该接口的子类匿名对象
匿名内部类的本质是继承类或者实现了接口的子类匿名对象
举例子:
interface Inter {
public abstract void show();
}
class Outer {
public void method(){
//方法1
new Inter(){
public void show(){
System.out.println("show");
}
}.show();
//方法2
Inter i = new Inter(){//多态
public void show(){
System.out.println("show");
}
};
i.show();
}
}
class InnerClassDemo6 {
public static void main(String[] args){
Outer o = new Outer();
o.method();
}
}
程序结果:打印show
(9)匿名内部类在开发中的使用
我们在开发的时候,会看到抽象类,或者接口作为参数。
而这个时候,我们知道实际需要的是一个子类对象。
如果该方法仅仅调用一次,我们就可以使用匿名内部类的格式简化。
举例子:
interface Person {
public abstract void study();
}
class PersonDemo {
//接口名作为形式参数
//其实这里需要的不是接口,而是该接口的实现类的对象。
public void method(Person p){
p.study();
}
}
//实现类
class Student implements Person {
public void study(){
System.out.println("Good Good Study,Day Day Up");
}
}
class InnerClassTest2{
public static void main(String[] args){
//测试1(容易理解):接口名作为形式参数
PersonDemo pd = new PersonDemo();
Person p = new Student();
pd.method(p);// Good Good Study,Day Day Up
System.out.println("----------");
//测试2(代码简洁):匿名内部类在开发中的使用
//不必像方法1,还要做个接口的实现类Student
//匿名内部类的本质是继承类或者实现了接口的子类匿名对象
pd.method(new Person(){
public void study(){
System.out.println("好好学习,天天向上");
}
});
}
}
注意:以上代码如果使用匿名内部类实现是不需要Student类的。
(10)匿名内部类的面试题(补齐代码)
要求补齐代码,输出HelloWorld
interface Inter{ void show();}
class Outer{ //补齐代码}
class OuterDemo{
public static void main(String[] args){
Outer.method().show();
}
}
实现:
interface Inter{
void show();
//默认修饰符public abstract
}
class Outer {//补齐代码
public static Inter method(){
//子类对象--子类匿名对象
return new Inter(){
public void show(){
System.out.println("HelloWorld");
}
};
}
}
class InnerClassTest3 {
public static void main(String[] args){
Outer.method().show();//链式编程
/*
1:Outer.method()可以看出method()应该是Outer中的一个静态方法
2:Outer.method().show()可以看出method()方法的返回值是一个对象。
又由于接口Inter中有一个show()方法,所以我认为method()方法的返回值类型
是一个接口。
*/
}
}