当多个类中出现相同功能,但是功能主体不同,这时可以向上抽取,但只抽取功能定义,不抽取功能主体。
abstract:抽象。只修饰类和方法,不能修饰变量。
1、抽象方法一定定义在抽象类中。
2、抽象类和抽象方法一定由abstract修饰。
3、抽象类不能用New创建对象,因为无意义。
4、抽象方法若要被调用,必须被重写子类中所有方法后建立子类对象调用,如果该子类只覆盖了部分,那么该子类还是一个抽象类。
abstract class student
{
abstract void study(); //这里没有大括号,即没有方法体
abstract void study1();
}
abstract class BaseStudent extends student //由于没有重写全部方法,只能把这个类也变抽象类。
{
void study()
{
System.out.println("baseStudent is studying");
}
}
既然抽象类里的东西都没有什么意义,为什么要用抽象呢?
定义成抽象类后,继承它的子类就必须重写里面的抽象方法否则子类也成抽象类变成无意义的东西了,这就保证了某类事物一定会具有某个功能,只是具体细节不同。比如说犬类,一定有叫这个功能,但具体怎么叫不同,狼会嚎,狗会汪,如果某天发现一个新品种,继承于抽象的犬类后必须先把它是怎么叫的定义出来,再加其它描述。
抽象类中可以有不抽象的方法。所谓“抽象”无非就是看不懂。当有通用的功能时直接定义在抽象类中即可。子类直接用。
抽象类和一般类没太大区别。只是有一些不确定的部分,这些不确定部分又是这类事物共有的属性,需要明确出现但又无法定义主体。用抽象的方式。
区别1:抽象类不能实例化。
区别2:类型多了个抽象方法。
抽象类中可以不定义抽象方法,这样做仅仅是不让该类建立对象。
小练习:
/**
需求:获取一段程序运行的时间。
原理:获取程序开始和结束的时间并相减。
获取时间:System.currentTimeMillis();
当代码完成优化后,就可以解决这类问题,
这种方式被称作“模版方法设计模式”
模版方法设计模式:定义功能时,功能的一部分是确定的,一部分是不确定的,而且确定的部分在使用不确定的部分。
这时将不确定的部分暴露出去,由该类的子类去完成。
*/
abstract class GetTime
{
public final void GetTime() //加final目的是不让子类复写这个方法,因为这个方法是因定的,用来获取时间的,不需要你复写啥。
{
long start = System.currentTimeMillis();
runcode();
long end = System.currentTimeMillis();
System.out.println("毫秒:"+(end-start))
}
public abstract void runcode();
}
/**
要计算哪个程序的时间,就写在下面复写的函数里。
*/
class subTime extends GetTime
{
public void runcode ()
{
}
}
class GetTimeDemo
{
public static void main(String[] args)
{
subTime gt = new subTime(); //建立的是子类对象,子类对象调用的是父类方法,当运行到runcode()时又调用的是被子类所复写的runcode();
gt.GetTime(); //这样做既保证了代码可以后来添加,又保证了调用的是添加后的代码
}
}
抽象
初期理解:抽象类中可有抽象方法也可有非抽象方法,当抽象类中的方法全部是抽象方法时,该类就可以用接口的形式表示。
interface用来定义接口,接口里边的成员必须全是抽象的。
格式特点:
1、接口中常见定义:常量和抽象方法。
2、接口中成员都有固定修饰符。 由于这些修饰符是固定的,所以少写了final或者某个修饰词时会被自动加上。
常量:public static final
方法:public abstract
3、接口中的成员都是public。
4、接口是不可以创建对象的,因为里面有抽象方法。只有当子类将其中 全部 抽象方法 重写后才可以创建对象,否则子类也是一个抽象类。
5、一个类可以同时实现多个接口,称为接口的多实现 。java不支持多继承的原因是担心两个父类中的方法有重复导致子类调用时出问题。
但多继承时继承的方法都有方法体但多实现时的方法都没有方法体。如果一个类实现多个接口时有相同定义的方法只覆盖一次即可。
interface Inter
{
public static final X=3; //注意常量要大写。
pulbic abstract void show();
}
interface Inter2
{
public abstract void method();
}
class Test implements Inter,Inter2 //类实现接口,用关键字implement
{
public void show(){}
}
class InterDemo
{
public static void main(String[] args)
{
Test t = new Test();
//下面三条都对,只要不赋值就行。
System.out.println(t.X);
System.out.println(Test.X);
System.out.println(Inter.X);
}
}
6、一个类在继承一个类的同时还可以实现多个接口。先继承再实现,可以扩展一个类的功能。
如:class Test extends Demo implements Inter,Inter2
7、类与类间是继承关系,类与接口之间是实现关系,接口与接口之间是继承关系。
8、接口之间存在多继承。因为没有方法体就不冲突。
interface A
{
public abstract void mathodA();
}
interface B extends A
{
public abstract void mathodB();
}
interface C extends B //8、如果B没有继承A的话这里C可以直接继承A,B
{
public abstract void mathodC();
}
class Demo7 implements C //这里虽然只说实现C但要实现三个功能。
{
public void mathodA(){}
public void mathodB(){}
public void mathodC(){}
}
接口的优点:
1、降低耦合性
2、是对外暴露的规则
3、可以对程序实现扩展。这种扩展是体系之外的扩展。比如有的狗会模仿人说话,但这个功能并不是所有狗都具备的,就把说话这个功能放在一个接口里。
那么狗子类在继承犬这个父类后就可以通过多加一个实现说话接口来实现说话这个功能。“说话”并非犬类天生该有的功能而是作为一种扩展功能附加的。
接口与继承的区别:
继承:你属于我。
接口:你是我的一个附加能力。
基本功能定义在类中,扩展功能定义在接口中。
多态:某一类事物具有多种表现形态。
/**
多态:同一事物有多种称呼形态。
函数的重写覆盖就是函数的多态性体现。
猫 x = new 猫();
动物 x = new 猫();
1、多态的体现
父类的引用指向自己的子类对象,这就是多态的代码体现形式 anmial x = new Cat();
或者说,父类的引用也可以接收自己的子类对象。
2、多态的前提
必须是类与类之间有关系,要么继承,要么实现(接口)。
前期预先定义功能后期定义子类去实现功能,并把子类作为参数传递进来,这就是扩展。
3、多态的好处
大大提高了程序的扩展性。
4、弊端
提高了扩展性,但是只能使用父类的引用访问父类中成员,而不能用子类。
5、多态的应用
6、多态代码特点
abstract class Animal
{
public abstract void eat();
}
class Cat extends Animal
{
public void eat()
{
System.out.println("eating fish");
}
public void fly()
{
System.out.println("cat is fling");
}
}
class Dog extends Animal
{
public void eat()
{
System.out.println("eating bone");
}
public void defance()
{
System.out.println("dog is defanting");
}
}
class Demo
{
public static void main(String[] args)
{
//Animal c = new Cat();这就是多态的代码体现。
fun(new Cat()); // Animal a =new Cat();
/*
Animal c = new Cat();
如果想要调用猫独有的功能团怎么办?
向下转型。
Cat a = (Cat)c;
a.fly();
但是下面这样是不允许的:
Animal ani= new Animal();
Cat a = (Cat )ani;
a.fly();
不能强制将动物变成猫,因为动物里没有fly方法。
只有当,父类的引用指向子类对象时,该引用可以被提升,也可以被转换。
多态自始至终都是子类在发生变化。
*/
}
public static void fun(Animal a)
{
a.eat();
}
}
/*
*/
abstract class Student
{
public abstract void study();
public void sleep()
{
System.out.println("躺着睡");
}
}
class BaseStudent extends Student
{
public void study()
{
System.out.println("BaseStudent study");
}
public void sleep()
{
System.out.println("站着睡");
}
}
class AdvStudent extends Student
{
public void study()
{
System.out.println("AdvStudent study");
}
}
class Dosth
{
public static void doStudent(Student stu)
{
stu.sleep();
stu.study();
}
}
class StudentDemo
{
public static void main(String[] args)
{
Dosth first = new Dosth();
first.doStudent(new BaseStudent());
first.doStudent(new AdvStudent());
}
}
/*
多态中非静态成员函数的特点:
1、在编译时期,参阅引用型变量所属的类中是否有调用的方法,如果没有则编译失败。
2、在运行时期,参阅对象所属的类中是否有调用的方法,
成员函数在多态调用时,编译看左边,运行看右边
而非静态成员变量在多态调用时,无论编译还是运行都看左边。
开发中没人去覆盖静态。以上针对的都是非静态。静态的啥都看左边。
**/
class Fu
{
public void method_1()
{
System.out.println("Fu method_1");
}
public void method_2()
{
System.out.println("Fu method_2");
}
}
class Zi extends Fu
{
public void method_1()
{
System.out.println("Zi method_1");
}
public void method_3()
{
System.out.println("Zi method_3");
}
}
class FuZiDemo
{
public static void main(String[] args)
{
Fu f = new Zi();
f.method_1(); //在运行时找的是对象中的方法1,也就是Zi中的方法1,所以会输出 Zi method_1
f.method_2();
// f.method_3(); 在编译时会报错,因为在编译时看的是引用型变量,Fu类里没有方法3
}
}
/**
多态:父类引用指向子类对象。
Objest obj = new Demo();
还可以向下转型:Demo d = (Demo) obj;
Object:所有对象的父类,根。
该类中定义所有对象都继承的功能。
Object.equal(Object obj)典型的多态应用。
equal 和== 在对象比较时候比的都是地址值。
在对象调用成员变量的时候比较的是变量的值。
注意,在复写函数时,要加个判断和类型转换语句。
*/
class Demo
{
private int num;
Demo(int num)
{
this.num = num;
}
public boolean equals(Object obj)
{
if (!(obj instanceof Demo)) //在复写函数时,要加个判断和类型转换语句。
return false;
Demo d = (Demo) obj;
return this.num==d.num;
}
}
class ObjectDemo
{
public static void main(String[] args)
{
Demo d1 = new Demo(4);
Demo d2 = new Demo(4);
System.out.println(d1.equals(d2));
}
}
/**
定义在一个类里面的类称为内部类。也称内置类。
内部类的访问规则:
1、内部类可以直接访问外部类中的成员,包括私有。因为内部类中持有外部类的引用。格式:外部类名.this
2、外部类要访问内部类必须建立内部对象。
3、直接访问内部类中的成员:Outer.Inner in = new Outer().new Inner();只是一种格式,记住即可。
4、当内部类在成员位置上就可以被成员修饰符所修饰。比如private,static。
当内部类变成静态时称为静态内部类,这时只能直接访问外部类中的静态成员,就有了局限。
在其它外部类中如何访问静态内部类中的 非静态方法 呢?
new Outer.Inner().fun() 通过建立这样一个对象调用
在其它外部类中如何访问静态内部类中的 静态方法 呢?
Outer.Inner.fun() 通过类名调用。
5、当内部类中定义了静态成员,则该内部类必须也是静态的。
6、当外部类中静态方法访问内部类时,内部类也必须是静态的。
7、内部类是外部类的成员时,可以被private或者static修饰,当其在函数内部时不能被修饰。
class Outer
{
int x=3;
void method()
{
/*
在函数里面定义了一个内部类,这里不能再用public 或static 修饰,因为它已经在函数里面了。
private是成员修饰符,而它已经不是成员了,它是一个局部内部类的形式存在。由于它不能被
static 修饰,而里面的函数如果被static修饰的话外边的它也得被static 修饰,所以里边的方法
也不能被static 修饰。
*/
class Inner
{
void function() //
{
System.out.println(Outer.this.x);//这里还是可以访问外部的x
}
}
new Inner.function();
}
}
8、内部类直接访问外部类中的成员,因为它持有外部类中的引用。
但是它不能直接访问它所在的局部变量,除非该局部变量被final修饰了。
class Outer
{
int x=3;
void method()
{
int y=9;
class Inner
{
void function()
{
System.out.println(y); //这里不能直接访问y ,需要在 y 那行声明一个 final.
}
}
new Inner.function();
}
}
9、内部类一般是这样写的,先私有再给个接口。
class Body
{
private class Heart
{
}
public void show()
{
new Heart();
}
}
*/
class Outer
{
private int x = 3;
static class Inner
{
int x = 4;
static void fun()
{
int x = 6;
System.out.println("Inner :" +x);//直接打印是6,写this.x结果是4,写(Outer.this.x)结果是3
}
}
class Inner2
{
void show()
{
System.out.println("Inner2 show");
}
}
public static void later()
{
Inner.fun();
}
void method()
{
Inner in = new Inner();
in.fun();
}
public void Print()
{
System.out.println(x);
}
}
class OutDemo
{
public static void main(String[] args)
{
Outer ot = new Outer();
ot.Print();
ot.method();
Outer.later();
}
}