抽象类
描述一个事物,却没有足够信息,这时就将这个事物称为抽象事物。面对抽象的事物,虽然不具体,但是可以简单化,不用面对具体的事物。(这样其实更方便于面对同一类对象)
抽象类:笼统,模糊,看不懂,不具体!
抽象类也是不断地向上抽取而来的。但是只抽取了方法声明,并不抽取具体的方法实现。只在乎有哪些功能,具体功能实现内容,由不同子类完成。抽象类中,可以定义抽象内容让子类实现,也可以定义非抽象内容让子类直接使用。它里面定义的都是一些体系中的基本内容。
抽象类的特点:
1.抽象方法只能定义在抽象类中,抽象类和抽象方法都需要用abstract来修饰。(abstract可以描述类和方法,不可以描述变量)
2.抽象类中,也可以定义非抽象方法。
3.抽象方法只定义方法声明,并不定义方法实现。
4.抽象类不能实例化。不能用new关键字创建对象。
5.只有子类继承抽象类并覆盖了抽象类中的所有抽象方法后,子类才能具体化(实例化),子类才可以创建对象。否则,该子类还是一个抽象类。(好处:强制抽象类的子类具备并实现所有的抽象方法,才能实例化。)
- abstract class 犬科{
- abstract void 吼叫();
- }
- class 狗 extends 犬科{
- void 吼叫(){
- System.out.println("汪汪");
- }
- }
- class 狼 extends 犬科{
- void 吼叫(){
- System.out.println("嗷嗷");
- }
- }
- class AbstractDemo{
- public static void main(String[] args){
-
- }
- }
关于抽象类,这里注明几个细节问题:
1.抽象类中有构造函数吗?
有。抽象类的构造函数虽然不能给抽象类对象实例化,因为抽象类不能创建对象,
但是抽象类有子类,它的构造函数可以给子类对象实例化。 2.抽象类和一般类的异同?
相同点:
抽象类和一般类都是用来描述事物的,都在内部定义了成员,进行属性和行为的描述。
不同点;
a.抽象类描述事物的信息不具体。
一般类描述事物的信息具体。
b.一般类中不能定义抽象方法,只能定义非抽象方法。
抽象类中可定义抽象方法,同时也可以定义非抽象方法。
c.一般类可以被实例化。
抽象类不可以被实例化。
3.抽象类一定是个父类吗?
是的。因为需要子类覆盖所有抽象方法后,才可以对子类实例化,使用这些方法。
abstract class Demo extends Fu{}
class Sub extends Demo{}
4.抽象类中可以不定义抽象方法吗?
可以。但是很少见,目的就是不让该类创建对象。AWT的适配器对象就是这种类。
通常这个类中的方法有方法体,但是却没有内容。
- abstract class Demo{
- void show1()
- {}
- void show2()
- {}
- }
5.抽象关键字abstract和哪些关键字不能共存呢?
final: final修饰类或者方法不可以被继承或覆盖,abstract修饰的类或方法必须有子类或被覆盖。
private:私有的方法,子类无法访问到,不叫覆盖。(public不能覆盖private。final不能被覆盖。)
static:静态区中的内容不需要对象,直接类名调用,抽象类名调用抽象方法没有意义。 需求:公司中程序员有姓名,工号,薪水,工作内容。
项目经理除了有姓名,工号,薪水,还有奖金,工作内容。
对给出需求进行数据建模。
分析:
程序猿:
属性:姓名,工号,薪水
行为:工作内容
项目经理:
属性:姓名,工号,薪水,还有奖金
行为:工作内容
两者不存在所属关系,但是有共性内容,可以向上抽取。
两者的共性类型是什么?雇员。
雇员:
属性:姓名,工号,薪水
行为:工作内容
- abstract class Employee{
- private String name;
- private String id;
- private double pay;
- Employee(String name, String id, double pay){
- this.name = name;
- this.id = id;
- this.pay = pay;
- }
- public abstract void work();
- }
- class Programmer extends Employee{
- Programmer(String name, String id, double pay){
- super(name,id,pay);
- }
- public void work(){
- System.out.println("code");
- }
- }
- class Manager extends Employee{
- private double bonus;
- Manager(String name, String id, double pay, double bonus){
- super(name,id,pay);
- this.bonus = bonus;
- }
- public void work(){
- System.out.println("manage");
- }
- }
- class AbstractDemo{
- public static void main(String[] args){
- new Manager("lichunchun","SA14010001",200000,5000).work();
- }
- }
模板方法设计模式 解决的问题:当功能内部一部分实现时确定,一部分实现是不确定的。这时可以把不确定的部分暴露出去,让子类去实现。
- abstract class GetTime{
- public final void getTime(){
- long start = System.currentTimeMillis();
- code();
- long end = System.currentTimeMillis();
- System.out.println("runtime : "+(end - start)+" ms");
- }
- public abstract void code();
- }
- class SubDemo extends GetTime{
- public void code(){
- for(int x = 0; x < 1000; x++){
- System.out.println("x");
- }
- }
- }
- class TemplateDemo{
- public static void main(String[] args){
- new SubDemo().getTime();
- }
- }
接口
- abstract class AbsDemo{
- public abstract void show1();
- public abstract void show2();
- }
抽象类中所有的方法都是抽象的。这时,可以把抽象类用另一种形式来表示:接口。初期可以理解为接口是特殊的抽象类。
定义接口使用的关键字不是class,是interface。
接口中包含的成员,最常见的有全局常量、抽象方法。
注意:接口中的成员都有固定的修饰符,即使不写,编译器也会自动补全。 成员变量:public static final
成员方法:public abstract
共性:成员都是public修饰的。
- interface Inter{
- public static final int NUM = 4;
- public abstract void show1();
- public abstract void show2();
- }
- class Demo implements Inter{
- public void show1(){}
- public void show2(){}
- }
- class InterfaceDemo{
- public static void main(String[] args){
- new Demo();
- }
- }
接口的特点:
1.接口不可以实例化。因为接口中的方法都是抽象方法。
2.接口的子类必须实现了接口中所有的抽象方法后,该子类才可以实例化。否则,该子类还是一个抽象类。
3.接口是用来被实现的。
类与类之间存在着继承关系,类与接口中间存在的是实现关系。继承用extends,实现用implements。- class Fu1{
- <span style="white-space:pre"> </span>void show(){
- sop("Fu1 show");
- }
- }
- class Fu2{
- void show(){
- sop("Fu2 show");
- }
- }
- class Zi extends Fu1,Fu2{
-
- }
- new Zi().show();
Java中不直接支持多继承,而是对该机制进行改良。通过接口来解决问题。将多继承转换成了多实现,在子类具体实现时,子类中才有方法主体,不会出现调用的不确定性。- interface InterA{
- public abstract void show();
- }
- interface InterB{
- public abstract void show();
- }
- class SubInter implements InterA,InterB{
- public void show(){
- System.out.println("inter show");
- }
- }
- class InterfaceDemo2{
- public static void main(String[] args){
- SubInter in = new SubInter();
- in.show();
- }
- }
其实,我们可以形象地将继承和实现比喻为只认一个爹,但可以有很多个叔叔,只不过这里的叔叔没有方法主体。- class Fu{
- void show(){
- System.out.println("fu show");
- }
- }
- interface Inter{
- public abstract void show1();
- }
- class Zi extends Fu implements Inter{
- public void method(){
- System.out.println("zi method");
- }
- public void show1(){
- System.out.println("zi show1");
- }
- }
- class InterfaceDemo3{
- public static void main(String[] args){
- Zi z = new Zi();
- z.show();
- z.show1();
- z.method();
- }
- }
类与类之间:单继承关系。is a,是什么。
类与接口之间:实现关系。like a,像什么。
接口与接口之间:多继承关系。- interface Inter1{
- public abstract void show1();
- }
- interface Inter2{
- public abstract void show2();
- }
- interface Inter3 extends Inter1,Inter2{
- public abstract void show3();
- }
- class Inter implements Inter3{
- public void show1(){}
- public void show2(){}
- public void show3(){}
- }
为了方便创建Inter接口的子类对象,可以用一个类先把接口中的所有方法都空实现。该类创建对象没有意义,所以可以将该类抽象。这就是传说中的没有抽象方法的抽象类。- interface Inter{
- public abstract void show1();
- public abstract void show2();
- public abstract void show3();
- public abstract void show4();
- }
- abstract class Demo implements Inter{
-
-
- public void show1(){}
- public void show2(){}
- public void show3(){}
- public void show4(){}
- }
- class DemoA extends Demo {
- public void show1(){
- System.out.println("show1");
- }
- }
- class DemoB extends Demo {
- public void show3(){
- System.out.println("show3");
- }
- }
- class InterfaceDemo4{
- public static void main(String[] args){
-
- }
- }
接口的思想: 笔记本电脑的USB接口。 1.接口的出现扩展了功能。 2.接口其实就是暴露出来的规则。 3.接口的出现降低了耦合性。解耦~ 接口的出现,产生了两方:一方在使用接口(笔记本),一方在实现接口(USB鼠标)。 开发顺序:接口->使用接口的类->实现接口的类,这其中,使用接口的类面向的是接口。
- class Mouse{}
- interface USB{}
- class NewMouse extends Mouse implements USB{}
抽象类:用于描述事物的共性基本功能,是一个体系单元的共性内容的向上抽取。 接口:用于定义的都是事物的额外扩展功能。 共性:都是不断向上抽取的结果。 抽象类和接口的区别: 1.类与类之间是继承关系,而且只能单继承 类与接口之间是实现关系,而且可以多实现 2.抽象类中可以定义抽象和非抽象方法,子类可以直接使用,或者覆盖使用。 接口中定义的都是抽象方法,子类必须全部实现才可以使用。 3.抽象类使用的是 is a 关系 接口使用的是 like a 关系 4.抽象类的成员修饰符可以自定义(除了abstract部分前面说的那些限制条件) 接口中的成员修饰符都是固定的。(public static final 和 public abstract) 5.抽象类有构造函数 接口没有构造函数- abstract class 犬{
- public abstract void 吃();
- public abstract void 叫();
- }
- interface 缉毒
- {
- public abstract void 缉毒();
- }
- class 缉毒犬 extends 犬 implements 缉毒{
- public void 吃(){}
- public void 叫(){}
- public void 缉毒(){}
- }
多态 下面我们再来说面向对象核心思想的最后一个特征:多态。
多态在程序中的体现:父类的引用或者接口的引用指向了子类的对象。
多态的好处:提高了代码的扩展性。
多态的弊端:不能使用子类的特有方法。(即访问的局限性)
多态的前提:
1.必须有关系,继承,实现。
2.通常有覆盖。
多态的出现思想上也做着变化:以前是创建对象并指挥对象做事情。有了多态以后,我们可以找到对象的共性类型,直接操作共性类型做事情即可,这样可以指挥一批对象做事情,即通过操作父类或接口实现。
- abstract class Animal{
- public abstract void eat();
- }
- class Dog extends Animal{
- public void eat(){
- System.out.println("bones");
- }
- public void lookHome(){
- System.out.println("look home");
- }
- }
- class Cat extends Animal{
- public void eat(){
- System.out.println("fish");
- }
- public void catchMouse(){
- System.out.println("catch mouse");
- }
- }
- class Pig extends Animal{
- public void eat(){
- System.out.println("si liao");
- }
- public void gongDi(){
- System.out.println("gong di");
- }
- }
- class DuotaiDemo{
- public static void main(String[] args){
-
-
-
-
- method(new Cat());
- }
-
- public static void method(Animal a){
- a.eat();
- }
- }
向上向下转型 向上转型好处:隐藏了子类型,提高了代码的扩展性。
向上转型弊端:只能使用父类中的功能,不能使用子类特有功能。功能被限定了。
如果不需要面对子类型,通过提高扩展性,或者使用父类的功能即可完成操作,就使用向上转型。
向下转型好处:可以使用子类型的特有功能。
向下转型弊端:面对具体的子类型。向下转型有风险。容易发生ClassCastException。只要转换类型和对象类型不匹配就会发生。想要安全,必须要进行判断。
判断一个对象是否匹配某一个类型,需要使用一个关键字 instanceof ,用法 对象 instanceof 类型。
什么时候用向下转型:需要使用子类型的特有方法时。但一定要用 instanceof 判断。
- abstract class Animal{
- public abstract void eat();
- }
- class Dog extends Animal{
- public void eat(){
- System.out.println("bones");
- }
- public void lookHome(){
- System.out.println("look home");
- }
- }
- class Cat extends Animal{
- public void eat(){
- System.out.println("fish");
- }
- public void catchMouse(){
- System.out.println("catch mouse");
- }
- }
- class DuotaiDemo2{
- public static void main(String[] args){
- Animal a = new Cat();
-
- if (!(a instanceof Dog)){
- System.out.println("类型不匹配");
- return;
- }
- Dog d = (Dog)a;
- d.eat();
- d.lookHome();
- }
- }
转型过程中,至始至终,只有子类对象这一个实例在做着类型的变化。 练习:
毕姥爷:讲课。钓鱼。
毕老师 extends 毕姥爷:讲课。看电影。
要求体现多态,要看到向上转型,向下转型。
- class 毕姥爷{
- public void 讲课(){
- System.out.println("管理");
- }
- public void 钓鱼(){
- System.out.println("钓鱼");
- }
- }
- class 毕老师 extends 毕姥爷{
- public void 讲课(){
- System.out.println("java");
- }
- public void 看电影(){
- System.out.println("看电影");
- }
- }
- class DuoTaiTest{
- public static void main(String[] args){
-
- 毕姥爷 x = new 毕老师();
- x.讲课();
- x.钓鱼();
-
- 毕老师 y = (毕老师)x;
- y.讲课();
- y.钓鱼();
- y.看电影();
- }
-
- }
多态在子父类成员上的特点 多态中对成员的调用。
1.成员变量。
当子父类中出现同名成员变量时,
多态调用时,只看调用该成员变量的引用所属类中的成员变量。
简单说:无论编译或者运行,都看等号左边的就哦了。
(编译时不产生对象,只检查语法错误)
2.成员函数。
当子父类中出现一模一样的函数时,多态调用,
编译时,看的是引用变量所属的类中的方法。(编译时,还没有创建对象)
运行时,看的是对象所属的类中的方法。(注意,此时父类方法,子类都已继承,若一模一样,则子类的覆盖)
简单说:编译看左边,运行看右边。
成员方法动态绑定到当前对象上。
3.静态方法
当子父类中出现一模一样的函数时,多态调用,
编译和运行是看引用变量所属的类中的方法。
简单说:编译运行都是看左边。
其实,真正调用静态方法是不需要对象的,直接类名调用。
因为静态方法绑定到该方法所属的类上,不所属于对象。
注:
- class Fu{
- int num = 4;
- void show(){
- System.out.println("fu show");
- }
- static void staticMethod(){
- System.out.println("fu static method");
- }
- }
- class Zi extends Fu{
- int num = 6;
- void show(){
- System.out.println("zi show");
- }
- static void staticMethod(){
- System.out.println("zi static method");
- }
- }
- class DuoTaiDemo3{
- public static void main(String[] args){
- Fu f = new Zi();
- System.out.println(f.num);
-
- f.show();
-
- f.staticMethod();
- }
- }
4.this.成员变量 始终代表的是本类引用。
执行哪个类的成员函数,那个成员函数中的this就代表那个类的引用。
- class Fu{
- int num = 5;
- void show(){
- System.out.println("num = "+this.num);
- }
- }
- class Zi extends Fu{
- int num = 6;
- }
- class DuoTaiTest2{
- public static void main(String[] args){
- Fu f = new Zi();
- f.show();
- }
- }
- class Fu{
- int num = 5;
- void show(){}
- }
- class Zi extends Fu{
- int num = 6;
- void show(){
- System.out.println("num = "+this.num);
- }
- }
- class DuoTaiTest2{
- public static void main(String[] args){
- Fu f = new Zi();
- f.show();
- }
- }
多态练习:USB接口。-
-
- interface USB{
- public abstract void open();
- public abstract void close();
- }
- class NoteBook{
- public void run(){
- System.out.println("NoteBook run");
- }
-
-
-
-
- public void useUSB(USB usb){
- if (usb != null){
- usb.open();
- usb.close();
- }
- }
- }
- class MouseByUSB implements USB{
- public void open(){
- System.out.println("mouse open");
- }
- public void close(){
- System.out.println("mouse close");
- }
- }
- class KeyBoardByUSB implements USB{
- public void open(){
- System.out.println("keyboard open");
- }
- public void close(){
- System.out.println("keyboard close");
- }
- }
- class USBTest{
- public static void main(String[] args){
- NoteBook book = new NoteBook();
- book.run();
- book.useUSB(null);
- book.useUSB(new MouseByUSB());
- book.useUSB(new KeyBoardByUSB());
- }
- }
Object类 Object:所有类的直接或者间接父类,Java认为所有的对象都具备一些基本的共性内容,这些内容可以不断的向上抽取,最终就抽取到了一个最顶层的类中的,该类中定义的就是所有对象都具备的功能。
具体方法:
1,boolean equals(Object obj):用于比较两个对象是否相等,其实内部比较的就是两个对象地址。
而根据对象的属性不同,判断对象是否相同的具体内容也不一样。
所以在定义类时,一般都会复写equals方法,建立本类特有的判断对象是否相同的依据。
2,String toString():将对象变成字符串。
默认返回的格式:类名@哈希值 = getClass().getName() + '@' + Integer.toHexString(hashCode())
为了对象对应的字符串内容有意义,可以通过复写,建立该类对象自己特有的字符串表现形式。
3,Class getClass():获取任意对象运行时的所属字节码文件对象
4,int hashCode():返回该对象的哈希码值。支持此方法是为了提高哈希表的性能。
通常equals,toString,hashCode,在应用中都会被复写,建立具体对象的特有的内容。
-
- class Person extends Object{
- private int age;
- private String name;
- Person(String name, int age){
- this.age = age;
- this.name = name;
- }
- public boolean equals(Object obj){
-
- if (this == obj)
- return true;
-
-
- if (!(obj instanceof Person))
- throw new ClassCastException("类型错误");
- Person p = (Person)obj;
-
- return this.name.equals(p.name) && this.age == p.age;
- }
-
-
-
- public String toString(){
- return "Person [ name = "+this.name+", age = "+this.age+" ]";
- }
- }
- class ObjectDemo{
- public static void main(String[] args){
- Person p1 = new Person("lisi",22);
- Person p2 = new Person("wanger",23);
- System.out.println(p1);
- System.out.println(p1.toString());
- System.out.println(p2.toString());
- System.out.println(p1.equals(p2));
- System.out.println(p1 == p2);
-
- }
- }
内部类 内部类其实就是将类定义到了另一个类内部。A类要直接访问B类中的成员时,可以将A类定义到B类中。作为B类的内部类存在。
访问规则:内部类可以直接访问外部类中的成员。外部类要想访问内部类,只能创建内部类的对象来访问。
当内部类定义在外部类中的成员位置上,可以使用一些成员修饰符修饰 private、static。
1:默认修饰符。
直接访问内部类格式:外部类名.内部类名 变量名 = 外部类对象.内部类对象;
Outer.Inner in = new Outer.new Inner();//这种形式很少用。
但是这种应用不多见,因为内部类之所以定义在内部就是为了封装。
想要获取内部类对象通常都通过外部类的方法来获取。这样可以对内部类对象进行控制。
2:私有修饰符。
通常内部类被封装,都会被私有化,因为封装性不让其他程序直接访问。
3:静态修饰符。
如果内部类被静态修饰,相当于外部类,会出现访问局限性,只能访问外部类中的静态成员。
注意;如果内部类中定义了静态成员,那么该内部类必须是静态的。
内部类编译后的文件名为:“外部类名$内部类名.java”;
- class Outer{
- private static int num = 4;
- class Inner{
- static final int count = 5;
- void show(){
- System.out.println(num);
- }
- }
- static class Inner2{
- void show2(){
- System.out.println("show2..."+num);
- }
- static void show3(){
- System.out.println("show3..."+num);
- }
- }
- public void method(){
- Inner in = new Inner();
- in.show();
- }
- }
- class InnerClassDemo{
- public static void main(String[] args){
- Outer out = new Outer();
- out.method();
-
-
- Outer.Inner in = new Outer().new Inner();
- in.show();
-
-
- Outer.Inner2 in = new Outer.Inner2();
- in.show2();
-
-
- Outer.Inner2.show3();
- }
- }
为什么内部类就可以直接访问外部类中的成员? 那是因为内部类持有了外部的引用:外部类名.this
- class Outer{
- int num = 2;
- class Inner{
- int num = 3;
- void show(){
- int num = 4;
- System.out.println("show..."+Outer.this.num);
- }
- }
- public void method(){
- new Inner().show();
- }
- }
- class InnerClassDemo2{
- public static void main(String[] args){
- new Outer().method();
- }
- }
内部类可以定义在外部类中的成员位置上,也可以定义在外部类中的局部位置上。 注意:局部内部类,只能访问被final修饰的局部变量。
因为非final修饰的,生命周期不够,因为局部变量消失,建立的对象还没有消失。
- class Outer{
- private int num = 4;
- public void method(){
- final int x = 5;
- class Inner{
- void show(){
- System.out.println("x = "+x);
- System.out.println("num = "+num);
- }
- }
- }
- }
- class InnerClassDemo2{
- public static void main(String[] args){
- new Outer().method();
- }
- }
匿名内部类 匿名内部类:简化书写的内部类。
前提:内部类需要继承或者实现外部的类或者接口。
格式:new 父类or接口名(){定义子类成员或者覆盖父类方法}.方法。
匿名内部类其实就是一个子类对象。
new Demo();---------后面加;号, 就是父类对象。
new Demo(){}-------后面加{}号,就是子类对象。
- abstract class Demo{
- abstract void show();
- }
- class Outer{
- private int num = 4;
- public void method(){
- new Demo(){
- public void show(){
- System.out.println("show..."+num);
- }
- }.show();
- }
- }
- class InnerClassDemo3{
- public static void main(String[] args){
- new Outer().method();
- }
- }
当出现多个方法时,可以如下使用:
- interface Inter{
- public abstract void show1();
- public abstract void show2();
- }
- class Outer{
- private int num = 2;
-
-
-
-
- public void method(){
-
-
-
- Inter in = new Inter(){
- public void show1(){}
- public void show2(){}
- };
- in.show1();
- in.show2();
- }
- }
- class InnerClassDemo4{
- public static void main(String[] args){
-
- new Inner();
- new InnerClassDemo4().new Inner();
- }
- class Inner{
-
- }
- public void show(){
- this.new Inner();
- }
- }
经典面试题:如下的1、2写法正确吗?有何区别?- class Outer{
-
-
-
- public void method(){
-
- new Object(){
- public void show(){
- System.out.println("show run");
- }
- }.show();
-
- Object obj = new Object(){
- public void show(){
- System.out.println("show run");
- }
- };
- obj.show();
- }
- }
- class InnerClassDemo5{
- public static void main(String[] args){
-
- }
- }
写法是正确,1和2都是在通过匿名内部类建立一个Object类的子类对象。
区别:
第一个可是编译通过,并运行。
第二个编译失败,因为匿名内部类是一个子类对象,当用Object的obj引用指向时,就被提升为了Object类型,而编译时检查Object类中是否有show方法,所以编译失败。
- class InnerClassDemo6 {
- +(static)class Inner{
- void show(){}
- }
- public void method(){
- this.new Inner().show();
- }
- public static void main(String[] args) {
- this.new Inner().show();
- }
- }
经典笔试题:通过匿名内部类补足Outer类中的代码。- interface Inter{
- void show();
- ]
- class Outer{
-
- }
- class InnerClassDemo6{
- public static void main(String[] args){
- Outer.method().show();
- }
- }
分析: Outer.method():意思是Outer中有一个名称为method的方法,而且这个方法是静态的。
Outer.method().show():当Outer类调用静态的method方法运算结束后的结果又调用了show方法,意味着:method()方法运算完一个是对象,而且这个对象是Inter类型的。
答案:
- interface Inter{
- void show();
- ]
- class Outer{
- public static Inter method(){
- return new Inter(){
- public void show(){}
- };
- }
- }
- class InnerClassDemo6{
- public static void main(String[] args){
- Outer.method().show();
- }
- }
此外,当函数的参数是接口类型引用时,匿名内部类还可以作为方法的参数进行传递。
- interface Inter{
- public abstract void show();
- }
- class InnerClassDemo7{
- public static void main(String[] args){
- function(new Inter(){
- public void show(){}
- });
- }
- public static void function(Inter in){
- in.show();
- }
- }
后面一片博客将会介绍,JavaSE面向对象中的异常、包。