3.面向对象(高级)

1.面向对象(基础)
2.面向对象(进阶)

3.1、继承

3.1.1、概念

继承是面向对象软件技术当中的一个概念,与多态、封装共为面向对象的三个基本特征。继承可以使得子类具有父类的属性和方法或者重新定义、追加属性和方法等。

3.1.2、继承关键字

继承可以使用 extends 和 implements 这两个关键字来实现继承,而且所有的类都是继承于java.lang.Object
当一个类没有继承的两个关键字,则默认继承object(这个类在 java.lang包中,所以不需要 import)祖先类。

  1. extends关键字

在 Java 中,类的继承是单一继承,也就是说,一个子类只能拥有一个父类,所以 extends 只能继承一个类。

public class Animal {
    private String name;
    private int id;
 
    public Animal(String Name, String id) {
        this.name= name;
        this.id= id;
    }
 
    public void eat(){
        System.out.println(name+"正在吃");
    }
 
    public void sleep(){
        System.out.println(name+"正在睡");
    }
     
    public void introduce() {
        System.out.println("大家好!我是"+ id+ "号"+ name+ ".");
    }
}
 
public  class   Mouse extends   Animal {
    public  Mouse(String myName, intmyId) {
        super(Name, Id);
    }
}
  1. implements关键字

implements 关键字可以变相的使Java具有多重继承(不是多继承)的特性,使用范围为类实现接口的情况,可以同时实现多个接口(接口之间采用逗号分隔)。

public interface A {
    public void eat();
    public void sleep();
}
 
public interface B {
    public void run();
}
 
public  class   C implements A,B {
 
}

3.1.3、语句格式:

class 子类名 extends 父类名{}
class 父类 { } class 子类 extends 父类 { }

3.1.4、注意

  1. 子类拥有父类的特征,而父类没有,父类更通用,子类更具体(特征包括属性和方法,自身的特性,拥有父类没有的)
  2. 使用extends继承父类,语句格式:class 子类名 extends 父类名{}
  3. 父类中一般只定义一般属性和方法(这个一般可以理解为是子类共有的,这就是父类更通用,而子类拥有其他的,所以子类更具体)
  4. 子类中通过super关键字来调用父构造方法
  5. 在子类中可以继承父类的哪些东西,哪些不可以继承——父类中public、protected修饰的属性和方法可以继承,private修饰的属性和方法不能被继承
  6. 规则:创建子类对象的时候,首先调用的是父类的无参构造方法创建一个父类对象
  7. 可以在子类中显示调用父类的有参构造方法
  8. 如果父类的属性均为private修饰,则可以通过共有的getter、setter方法来调用
  9. java只有单继承、多重继承,没有多继承

现今面向对象程式设计技巧中,继承并非以继承类别的“行为”为主,而是继承类别的“型态”,使得元件的型态一致。另外在设计模式中提到一个守则,“多用合成,少用继承”,此守则也是用来处理继承无法在执行期动态扩充行为的遗憾。

3.2、supper关键字

  1. 可以访问父类的构造方法
  2. 调用supper构造方法的代码,必须写在子类构造方法第一行
  3. 可以访问父类的属性
  4. 可以访问父类的方法

使用 super 来调用父类构造函数

class Animal {
    Animal() {
        System.out.println("animal is created");
    }
}

class Dog extends Animal {
    Dog() {
        super();
        System.out.println("dog is created");
    }
}

class TestSuper3 {
    public static void main(String args[]) {
        Dog d = new Dog();
    }
}

注意:如果没有使用super()或this(),则super()在每个类构造函数中由编译器自动添加。

我们知道,如果没有构造函数,编译器会自动提供默认构造函数。 但是,它还添加了super()作为第一个语句。
下面是super关键字的另一个例子,这里super()由编译器隐式提供。

class Animal {
    Animal() {
        System.out.println("animal is created");
    }
}

class Dog extends Animal {
    Dog() {
        System.out.println("dog is created");
    }
}

class TestSuper4 {
    public static void main(String args[]) {
        Dog d = new Dog();
    }
}

3.3、重写,重写与重载的区别

3.3.1、重写(Override)

  1. 重写是子类对父类的允许访问的方法的实现过程进行重新编写, 返回值和形参都不能改变。即外壳不变,核心重写
  2. 重写的好处在于子类可以根据需要,定义特定于自己的行为。 也就是说子类能够根据需要实现父类的方法。
  3. 重写方法不能抛出新的检查异常或者比被重写方法申明更加宽泛的异常
class Animal{
   public void move(){
      System.out.println("动物可以移动");
   }
}
 
class Dog extends Animal{
   public void move(){
      System.out.println("狗可以跑和走");
   }
}
 
public class TestDog{
   public static void main(String args[]){
      Animal a = new Animal(); // Animal 对象
      Animal b = new Dog(); // Dog 对象
 
      a.move();// 执行 Animal 类的方法
 
      b.move();//执行 Dog 类的方法
   }
}

3.3.2、方法的重写规则

  1. 参数列表必须完全与被重写方法的相同。
  2. 返回类型与被重写方法的返回类型可以不相同,但是必须是父类返回值的派生类(java5 及更早版本返回类型要一样,java7 及更高版本可以不同)。
  3. 访问权限不能比父类中被重写的方法的访问权限更低。例如:如果父类的一个方法被声明为 public,那么在子类中重写该方法就不能声明为 protected。
  4. 父类的成员方法只能被它的子类重写。
  5. 声明为 final 的方法不能被重写。
  6. 声明为 static 的方法不能被重写,但是能够被再次声明。
  7. 子类和父类在同一个包中,那么子类可以重写父类所有方法,除了声明为 private 和 final 的方法。
  8. 子类和父类不在同一个包中,那么子类只能够重写父类的声明为 public 和 protected 的非 final 方法。
  9. 重写的方法能够抛出任何非强制异常,无论被重写的方法是否抛出异常。但是,重写的方法不能抛出新的强制性异常,或者比被重写方法声明的更广泛的强制性异常,反之则可以。
  10. 构造方法不能被重写。
  11. 如果不能继承一个方法,则不能重写这个方法。

3.3.3、重载(Overload)

  1. 重载(overloading) 是在一个类里面,方法名字相同,而参数不同。返回类型可以相同也可以不同。
  2. 每个重载的方法(或者构造函数)都必须有一个独一无二的参数类型列表。 最常用的地方就是构造器的重载。 重载规则:
  3. 被重载的方法必须改变参数列表(参数个数或类型不一样); 被重载的方法可以改变返回类型; 被重载的方法可以改变访问修饰符;
  4. 被重载的方法可以声明新的或更广的检查异常; 方法能够在同一个类中或者在一个子类中被重载。 无法以返回值类型作为重载函数的区分标准。

3.3.4、重写与重载之间的区别

区别点 重载方法 重写方法
参数列表 必须修改 一定不能修改
返回类型 可以修改 一定不能修改
异常 可以修改 可以减少或删除,一定不能抛出新的或者更广的异常
访问 可以修改 一定不能做更严格的限制(可以降低限制)

3.3.5、总结

方法的重写(Overriding)和重载(Overloading)是java多态性的不同表现,重写是父类与子类之间多态性的一种表现,重载可以理解成多态的具体表现形式。

  • 方法重载是一个类中定义了多个方法名相同,而他们的参数的数量不同或数量相同而类型和次序不同,则称为方法的重载(Overloading)。
  • 方法重写是在子类存在方法与父类的方法的名字相同,而且参数的个数与类型一样,返回值也一样的方法,就称为重写(Overriding)。
  • 方法重载是一个类的多态性表现,而方法重写是子类与父类的一种多态性表现。
    3.面向对象(高级)_第1张图片

3.4、final关键字

  • final用于修饰属性、变量
  • final用于修饰类
  • final用于修饰方法

3.4.1.修饰类

当用final修饰一个类时,表明这个类不能被继承。也就是说,如果一个类你永远不会让他被继承,就可以用final进行修饰。final类中的成员变量可以根据需要设为final,但是要注意final类中的所有成员方法都会被隐式地指定为final方法。

在使用final修饰类的时候,要注意谨慎选择,除非这个类真的在以后不会用来继承或者出于安全的考虑,尽量不要将类设计为final类。

3.4.2.修饰方法

“使用final方法的原因有两个。

  • 第一个原因是把方法锁定,以防任何继承类修改它的含义;
  • 第二个原因是效率。在早期的Java实现版本中,会将final方法转为内嵌调用。但是如果方法过于庞大,可能看不到内嵌调用带来的任何性能提升。在最近的Java版本中,不需要使用final方法进行这些优化了。“

因此,如果只有在想明确禁止 该方法在子类中被覆盖的情况下才将方法设置为final的。

3.4.3.修饰变量

对于一个final变量即是常量,如果是基本数据类型的变量,则其数值一旦在初始化之后便不能更改;如果是引用类型的变量,则在对其初始化之后便不能再让其指向另一个对象。

3.4.4深入理解final

1.类的final变量和普通变量有什么区别?
  当用final作用于类的成员变量时,成员变量(注意是类的成员变量,局部变量只需要保证在使用之前被初始化赋值即可)必须在定义时或者构造器中进行初始化赋值,而且final变量一旦被初始化赋值之后,就不能再被赋值了。
2.被final修饰的引用变量指向的对象内容可变吗?
在上面提到被final修饰的引用变量一旦初始化赋值之后就不能再指向其他的对象,那么该引用变量指向的对象的内容可变吗?

答:引用变量被final修饰之后,虽然不能再指向其他对象,但是它指向的对象的内容是可变的。

3.final和static
  很多时候会容易把static和final关键字混淆,static作用于成员变量用来表示只保存一份副本,而final的作用是用来保证变量不可变。

3.5、抽象类

3.5.1、概念

  • 抽象类必须使用abstract class声明
  • 一个抽象类中可以没有抽象方法。抽象方法必须写在抽象类或者接口中。
    格式:
abstract class 类名{  // 抽象类            
}

3.5.2、抽象方法

只声明而未实现的方法称为抽象方法(未实现指的是:没有“{}”方法体),抽象方法必须使用abstract关键字声明。
格式:

abstract class 类名{  // 抽象类        
public abstract void 方法名() ;    // 抽象方法,只声明而未实现    
}

3.5.3、不能被实例化

在抽象类的使用中有几个原则:

  • 抽象类本身是不能直接进行实例化操作的,即:不能直接使用关键字new完成。
  • 一个抽象类必须被子类所继承,被继承的子类(如果不是抽象类)则必须覆写(重写)抽象类中的全部抽象方法。

3.5.4、常见问题

1.抽象类能否使用final声明?
不能,因为final属修饰的类是不能有子类的 , 而抽象类必须有子类才有意义,所以不能。
2.抽象类能否有构造方法?
能有构造方法,而且子类对象实例化的时候的流程与普通类的继承是一样的,都是要先调用父类中的构造方法(默认是无参的),之后再调用子类自己的构造方法。

3.5.5、抽象类和普通类的区别

  • 抽象类必须用public或protected修饰(如果为private修饰,那么子类则无法继承,也就无法实现其抽象方法)。 默认缺省为public
  • 抽象类不可以使用new关键字创建对象, 但是在子类创建对象时, 抽象父类也会被JVM实例化。
  • 如果一个子类继承抽象类,那么必须实现其所有的抽象方法。如果有未实现的抽象方法,那么子类也必须定义为 abstract类

3.6、接口

3.6.1、概念

如果一个类中的全部方法都是抽象方法,全部属性都是全局常量,那么此时就可以将这个类定义成一个接口。
定义格式:

interface 接口名称{        
全局常量 ;        
抽象方法 ;    
}

3.6.2、面向接口编程思想

这种思想是接口是定义(规范,约束)与实现(名实分离的原则)的分离。

优点:

  • 降低程序的耦合性
  • 易于程序的扩展
  • 有利于程序的维护

3.6.3、全局常量和抽象方法的简写

因为接口本身都是由全局常量和抽象方法组成 , 所以接口中的成员定义可以简写:
1、全局常量编写时, 可以省略public static final 关键字,例如:
public static final String INFO = “内容” ;
简写后: String INFO = “内容” ;
2、抽象方法编写时, 可以省略 public abstract 关键字, 例如:
public abstract void print() ;
简写后: void print() ;

3.6.4、接口的实现 implements

接口可以多实现
格式:

class 子类 implements 父接口1,父接口2...{        }    

以上的代码称为接口的实现。那么如果一个类即要实现接口,又要继承抽象类的话,则按照以下的格式编写即可:

class 子类 extends 父类 implements 父接口1,父接口2...{        }

3.6.5、接口的继承

接口因为都是抽象部分, 不存在具体的实现, 所以允许多继承,例如:

interface C extends A,B{        }

3.6.6、注意

如果一个接口要想使用,必须依靠子类。 子类(如果不是抽象类的话)要实现接口中的所有抽象方法。

3.6.7、接口和抽象类的区别

  1. 抽象类要被子类继承,接口要被类实现。
  2. 接口只能声明抽象方法,抽象类中可以声明抽象方法,也可以写非抽象方法。
  3. 接口里定义的变量只能是公共的静态的常量,抽象类中的变量是普通变量。
  4. 抽象类使用继承(extend)来使用, 无法多继承。
  5. 接口使用实现(implements )来使用, 可以多实现
  6. 抽象类中可以包含static方法,但是接口中不允许(静态方法不能被子类重写,因此接口中不能声明静态方法)
  7. 接口不能有构造方法,但是抽象类可以有

3.7、多态

3.7.1、概念

多态:就是对象的多种表现形式,(多种体现形态)

3.7.2、多态的体现

  • 对象的多态性,从概念上非常好理解,在类中有子类和父类之分,子类就是父类的一种形态 ,对象多态性就从此而来。
  • ps: 方法的重载 和 重写
    也是多态的一种, 不过是方法的多态(相同方法名的多种形态)。
  • 重载: 一个类中方法的多态性体现
  • 重写: 子父类中方法的多态性体现。

3.7.3、多态的使用:对象的类型转换

(父类引用指向子类对象)

类似于基本数据类型的转换:

  • 向上转型:将子类实例变为父类实例
    格式:父类 父类对象 = 子类实例 ;
    弊端:不能调用子类独有的方法,可以调用父类独有的和子类重写后的方法
Father father = new Son();
  • 向下转型:将父类实例变为子类实例
    格式:子类 子类对象 = (子类)父类实例
Son son = (Son)new Father();

3.8、instanceof

instanceof是Java中的二元运算符,左边是对象,右边是类;当对象是右边类或子类所创建对象时,返回true;否则,返回false。

这里说明下:
类的实例包含本身的实例,以及所有直接或间接子类的实例
instanceof左边显式声明的类型与右边操作元必须是同种类或存在继承关系,也就是说需要位于同一个继承树,否则会编译错误

  • 作用:
    判断某个对象是否是指定类的实例,则可以使用instanceof关键字

  • 格式:
    实例化对象 instanceof 类 //此操作返回boolean类型的数据

3.9、Object类

3.9.1、概念

Object类是所有类的父类(基类),如果一个类没有明确的继承某一个具体的类,则将默认继承Object类。
例如我们定义一个类:

 public class Person{ }

其实它被使用时 是这样的:

public class Person extends Object{ }

3.9.2、Object的多态

使用Object可以接收任意的引用数据类型

3.9.3、toString

建议重写Object中的toString方法。
此方法的作用:返回对象的字符串表示形式。
Object的toString方法, 返回对象的内存地址

3.9.4、equals

建议重写Object中的equals(Object obj)方法,

  • 此方法的作用:指示某个其他对象是否“等于”此对象。

Object的equals方法:实现了对象上最具区别的可能等价关系; 也就是说,对于任何非空引用值x和y ,当且仅当 x和y引用同一对象(
x == y具有值true )时,此方法返回true 。

equals方法重写时的五个特性:

  • 自反性 :对于任何非空的参考值x , x.equals(x)应该返回true 。
  • 对称性 :对于任何非空引用值x和y ,x.equals(y)应该返回true当且仅当y.equals(x)回报true 。
  • 传递性 :对于任何非空引用值x , y和z,如果x.equals(y)返回true且y.equals(z)返回true ,则 x.equals(z)应该返回true 。
  • 一致性 :对于任何非空引用值x和y ,多次调用x.equals(y)始终返回true或始终返回false ,前提是未修改对象上的equals比较中使用的信息。
  • 非空性 :对于任何非空的参考值x ,x.equals(null)应该返回false 。

3.10、内部类

3.10.1、概念

在Java中,可以将一个类定义在另一个类里面或者一个方法里面,这样的类称为内部类。
广泛意义上的内部类一般来说包括这四种:

  • 成员内部类
  • 局部内部类
  • 匿名内部类
  • 静态内部类

3.10.2、成员内部类

成员内部类是最普通的内部类,它的定义为位于另一个类的内部,形如下面的形式:

class Outer {    
    private double x = 0;         
    public Outer(double x) {        
    this.x = x;    
    }         
    class Inner {     //内部类        
        public void say() {            
            System.out.println("x="+x);        
        }    
    }
}

特点: 成员内部类可以无条件访问外部类的所有成员属性和成员方法(包括private成员和静态成员)。

  • 不过要注意的是,当成员内部类拥有和外部类同名的成员变量或者方法时,会发生隐藏现象,即默认情况下访问 的是成员内部类的成员。
  • 如果要访问外部类的同名成员,需要以下面的形式进行访问:
外部类.this.成员变量    
外部类.this.成员方法
//外部使用成员内部类
 Outter outter = new Outter();        
 Outter.Inner inner = outter.new Inner(); 

3.10.3、局部内部类

  • 局部内部类是定义在一个方法或者一个作用域里面的类,它和成员内部类的区别在于局部内部类的访问仅限于方法内或 者该作用域内。
  • 注意:局部内部类就像是方法里面的一个局部变量一样,是不能有public、protected、private以及static修饰符的

3.10.4、匿名内部类

匿名内部类由于没有名字,所以它的创建方式有点儿奇怪。创建格式如下:

new 父类构造器(参数列表)|实现接口(){       
//匿名内部类的类体部分      
}    

在这里我们看到使用匿名内部类我们必须要继承一个父类或者实现一个接口,当然也仅能只继承一个父类或者实现一 个接口。同时它也是没有class关键字,这是因为匿名内部类是直接使用new来生成一个对象的引用。 当然这个引用是隐式的。
注意:
在使用匿名内部类的过程中,我们需要注意如下几点:

  1. 使用匿名内部类时,我们必须是继承一个类或者实现一个接口,但是两者不可兼得,同时也只能继承一个类或 者实现一个接口。
  2. 匿名内部类中是不能定义构造函数的。
  3. 匿名内部类中不能存在任何的静态成员变量和静态方法。
  4. 匿名内部类为局部内部类,所以局部内部类的所有限制同样对匿名内部类生效。
  5. 匿名内部类不能是抽象的,它必须要实现继承的类或者实现的接口的所有抽象方法。
  6. 只能访问final型的局部变量

3.10.5、静态内部类

静态内部类也是定义在另一个类里面的类,只不过在类的前面多了一个关键字static。 静态内部类是不需要依赖于外部类对象的,这点和类的静态成员属性有点类似,并且它不能使用外部类的非static成员 变量或者方法.

3.11、 包装类

3.11.1、概述

在Java中有一个设计的原则“一切皆对象”,那么这样一来Java中的一些基本的数据类型,就完全不符合于这种设计思 想,因为Java中的八种基本数据类型并不是引用数据类型,所以Java中为了解决这样的问题,引入了八种基本数据类型 的包装类。
3.面向对象(高级)_第2张图片
以上的八种包装类,可以将基本数据类型按照类的形式进行操作。
但是,以上的八种包装类也是分为两种大的类型的:

  • Number:Integer、Short、Long、Double、Float、Byte都是Number的子类表示是一个数字。
  • Object:Character、Boolean都是Object的直接子类。

3.11.2、装箱和拆箱操作

以下以Integer和Float为例进行操作

  • 将一个基本数据类型变为包装类,那么这样的操作称为装箱操作。
  • 将一个包装类变为一个基本数据类型,这样的操作称为拆箱操作。

因为所有的数值型的包装类都是Number的子类,Number的类中定义了如下的操作方法,以下的全部方法都是进行拆箱的操 作。
3.面向对象(高级)_第3张图片

装箱操作: 在JDK1.4之前 ,如果要想装箱,直接使用各个包装类的构造方法即可,例如: int temp = 10 ;
// 基本数据类型 Integer x = new Integer(temp) ; // 将基本数据类型变为包装类
在JDK1.5,Java新增了自动装箱和自动拆箱,而且可以直接通过包装类进行四则运算和自增自建操作。例如: Float f
= 10.3f ; // 自动装箱 float x = f ; // 自动拆箱 System.out.println(f * f) ; // 直接利用包装类完成
System.out.println(x * x) ; // 直接利用包装类完成

3.11.3、字符串转换

使用包装类还有一个很优秀的地方在于:可以将一个字符串变为指定的基本数据类型,此点一般在接收输入数据上使用 较多。
在Integer类中提供了以下的操作方法:

public static int parseInt(String s);//将String变为int型数据 

在Float类中提供了以下的操作方法:

public static float parseFloat(String s);//将String变为Float 

在Boolean 类中提供了以下操作方法:

public static boolean parseBoolean(String s);//将String变为boolean 
....    ....

3.12、可变参数

一个方法中定义完了参数,则在调用的时候必须传入与其一一对应的参数,但是在JDK 1.5之后提供了新的功能,可以根 据需要自动传入任意个数的参数。

语法:

返回值类型 方法名称(数据类型…参数名称){
//参数在方法内部 , 以数组的形式来接收  
}   

注意:
可变参数只能出现在参数列表的最后。

3.13、递归

递归,在数学与计算机科学中,是指在方法的定义中使用方法自身。也就是说,递归算法是一种直接或者间接调用自身方 法的算法。
递归流程图如下:
3.面向对象(高级)_第4张图片

你可能感兴趣的:(#,java面向对象,J2EE全程学习,java,面向对象编程,多态,抽象类,接口)