2. 面向对象(三)

简介: 2. 面向对象(三)

2.12.3多态中的转型


体现:


  • 父类的引用可以指向子类的对象
  • 接口的引用可以指向实现类的对象


转型:


  • 向上转型


由子类类型转型为父类类型,或者由实现类类型转型为接口类型


向上转型一定会成功,是一个隐式转换


向上转型后的对象,将只能访问父类或者接口中的成员


  • 向下转型


由父类类型转型为子类类型,或者由接口类型转型为实现类类型


向下转型可能会失败,是一个显式转换


向下转型后的对象,将可以访问子类或者实现类中特有的成员


/*
* 向上转型
*   从子到父
*   父类引用指向子类对象
* 向下转型
*   从父到子
*   父类引用转为子类对象
*/
public class TestDemo {
  public static void main(String[] args) {
    //多态
    Animal a = new Cat(); //向上转型
    a.eat();
    //a.playGame();
    //多态的弊端:无法访问子类特有方法
    //现在我就想使用子类特有方法,怎么办呢?
    //创建子类对象就可以了
    /*
    Cat c = new Cat();
    c.eat();
    c.playGame();
    */
    //现在的代码虽然可以访问子类的特有功能,但是不合理
    //因为我们发现内存中有两个猫类的对象
    //这个时候,我们得想办法把多态中的猫对象还原
    //这个时候,就要使用多态中的转型了
    //父类引用转为子类对象
    Cat c = (Cat)a;
    c.eat();
    c.playGame();
  }
}
public class Cat extends Animal {
  public void eat() {
    System.out.println("猫吃鱼");
  }
  public void playGame() {
    System.out.println("猫捉迷藏");
  }
}
public class Animal {
  public void eat() {
    System.out.println("吃东西");
  }
}

2.12.4 instanceof关键字


针对于向下转型的。


如果向下转型不成功,会怎样?会有一个异常 ClassCastException


如何避免这种情况?在向下转型之前,我们先判断一下这个对象是不是要转型的类型怎么判断?


关键字 instanceof


Animal animal = new Dog();
if (animal instanceof Dog) {
    // 说明animal的确是一个Dog
}

如果一个类中重写了父类的某一个方法。此时:


如果用这个类的对象来调用这个方法,最终执行的是子类的实现。


如果用向上转型后的对象来调用这个方法,执行的依然是子类的实现。


向上转型后的对象,归根到底还是子类对象。


public class TestDemo {
  public static void main(String[] args) {
    //多态
    Animal a = new Cat(); //向下转型
    Cat c = new Cat();
    if(a instanceof Cat)
    c = a
    c.eat();
    c.playGame();
  }
}
public class Cat extends Animal {
  public void eat() {
    System.out.println("猫吃鱼");
  }
  public void playGame() {
    System.out.println("猫捉迷藏");
  }
}
public class Animal {
  public void eat() {
    System.out.println("吃东西");
  }
}

2.13 包


java的包,类似电脑系统中的文件夹,包里存放的是类文件。


当类文件很多的时候,通常会采用多个包进行存放管理,这种方式称为分包管理。


在项目中,我们将相同功能的类放到一个包中,方便管理。并且日常项目的分工也是以包作为边界。


类中声明的包必须与实际class文件所在的文件夹情况相一致,即类声明在a包下,则生成的.class文件必须在a文件夹下,否则,程序运行时会找不到类。


声明格式:


通常使用公司网址反写,可以有多层包,包名采用全部小写字母,多层包之间用”.”连接


类中包的声明格式:


package 包名.包名.包名…;


注意:声明包的语句,必须写在程序有效代码的第一行(注释不算)


package com.kaikeba
import java.util.Scanner;
import java.util.Random;

包的访问:


在访问类时,为了能够找到该类,必须使用含有包名的类全名(包名.类名)。


包名.包名….类名


java.util.Scanner
//带有包的类,创建对象格式:包名.类名 变量名 = new包名.类名();
java.util.Scanner scan = new java.util.Scanner(System.in);


类的简化访问


  • 要使用一个类,这个类与当前程序在同一个包中(即同一个文件夹中),或者这个类是java.lang包中的类时通常可以省略掉包名,直接使用该类。
  • 要使用的类,与当前程序不在同一个包中(即不同文件夹中),要访问的类必须用public修饰才可访问。


包的导入:


我们每次使用类时,都需要写很长的包名。很麻烦,我们可以通过import导包的方式来简化。


可以通过导包的方式使用该类,可以避免使用全类名编写(即,包类.类名)。


导包的格式:


import 包名.类名;


当程序导入指定的包后,使用类时,就可以简化了。


//导入包前的方式
//创建对象
java.util.Random r1 = new java.util.Random();
java.util.Random r2 = new java.util.Random();
java.util.Scanner sc1 = new java.util.Scanner(System.in);
java.util.Scanner sc2 = new java.util.Scanner(System.in);
//导入包后的方式
import java.util.Random;
import java.util.Scanner;
//创建对象
Random r1 = new Random();
Random r2 = new Random();
Scanner sc1 = new Scanner(System.in);
Scanner sc2 = new Scanner(System.in);

import导包代码书写的位置:在声明包package后,定义所有类class前,使用导包import包名.包名.类名;


2.14  访问权限修饰符


用来描述一个类、方法、属性、接口、枚举...能够被访问到的一个范围


访问权限一共有四种:


公开(public)/保护(protected)/包(default / package)/私有(private)


public > protected > default > private


对应的访问权限修饰符一共有三个:


public/protected/private


注:包权限没有访问权限修饰符,如果一个方法、属性、类...没有使用任意的访问权限修饰符来修饰,那么他的访问权限就是包权限

访问权限

可以修饰什么

可以访问的范围

private

类成员

只能在当前的类中访问

default

类成员、类

只能在当前的包中进行访问

protected

类成员

可以在当前的包中访问,也可以在跨包的子类中访问

public

类成员、类

可以在项目中任意的位置进行访问

public

protected

default

private

同一类中

同一包中(子类与无关类)

不同包的子类

不同包中的无关类


2.15 final关键字

修饰

意义

变量

这个变量的值不能改变,就是常量

表示是一个最终类,这个类无法被继承

方法

表示是一个最终方法,这个方法无法被重写


继承的出现提高了代码的复用性,并方便开发。但随之也有问题,有些类在描述完之后,不想被继承,或者有些类中的部分方法功能是固定的,不想让子类重写。


要解决上述的这些问题,需要使用到一个关键字final,final的意思为最终,不可变。final是个修饰符,它可以用来修饰类,类的成员,以及局部变量。


final使用:


final修饰类不可以被继承,但是可以继承其他类。


class XX {}
final class YY extends XX{} //可以继承XX类
class ZZ extends YY{} //不能继承Fu类


final修饰的方法不可以被覆盖,但父类中没有被final修饰方法,子类覆盖后可以加final。


class Father {
  // final修饰的方法,不可以被覆盖,但可以继承使用
    public final void method1(){}
    public void method2(){}
}
class Son extends Father {
  //重写method2方法
  public final void method2(){}
}

final修饰的变量称为常量,这些变量只能赋值一次。


final int i = 20;
i = 30; //赋值报错,final修饰的变量只能赋值一次


引用类型的变量值为对象地址值,地址值不能更改,但是地址内的对象属性值可以修改。


final Person s = new Person();
Person p2 = new Person();
p = p2; //final修饰的变量p,所记录的地址值不能改变
p.name = "小明";//可以更改p对象中name属性值


修饰成员变量,需要在创建对象前赋值,否则报错。(当没有显式赋值时,多个构造方法的均需要为其赋值。)


class Demo {
  //直接赋值
  final int m = 100;
  //final修饰的成员变量,需要在创建对象前赋值,否则报错。
  final int n; 
  public Demo(){
    //可以在创建对象时所调用的构造方法中,为变量n赋值
    n = 2020;
  }
}


注意:


抽象类可以用final来修饰吗?


不能!因为final表示这个类无法被继承。但是对于抽象类来说,如果无法被继承,则这个抽象类没有任何意义。


抽象方法可以用final修饰吗?


不能!因为final修饰的方法无法被重写。但是抽象方法又只能写在抽象类中。如果一个抽象方法用final来修饰了,此时这个方法将无法被非抽象子类重写,那这个子类就会有问题。


2.16 内部类


内部类概念


将类写在其他类的内部,可以写在其他类的成员位置和局部位置,这时写在其他类内部的类就称为内部类。其他类也称为外部类。


使用时机


在描述事物时,若一个事物内部还包含其他可能包含的事物,比如在描述汽车时,汽车中还包含这发动机,这时发动机就可以使用内部类来描述。


class 汽车 { //外部类
  class 发动机 { //内部类
}
}

2.16.1 成员内部类


成员内部类,定义在外部类中的成员位置。与类中的成员变量相似,可通过外部类对象进行访问。


定义格式


class 外部类 { 
  修饰符 class 内部类 {
    //其他代码
}
}

访问方式


外部类名.内部类名 变量名 = new 外部类名().new 内部类名();


案例:


public class InnerClass {
    //访问内部类
    public static void main(String[] args) {
        //创建内部类对象
        Body.Heart bh = new Body().new Heart();
        //调用内部类中的方法
        bh.jump();
    }
}
class Body {//外部类,身体
    private boolean life= true; //生命状态
    public class Heart { //内部类,心脏
        public void jump() {
            System.out.println("心脏在跳动");
            System.out.println("生命状态" + life); //访问外部类成员变量
        }
    }
}


2.16.2 局部内部类


定义在外部类方法中的局部位置。与访问方法中的局部变量相似,可通过调用方法进行访问。


定义格式


class 外部类 { 
  修饰符 返回值类型 方法名(参数) {
class 内部类 {
//其他代码
}
}
}


访问方式:在外部类方法中,创建内部类对象,进行访问


class Party {//外部类,聚会
  public void puffBall(){// 吹气球方法
    class Ball {// 内部类,气球
              public void puff(){
     System.out.println("气球膨胀了");
}
}
//创建内部类对象,调用puff方法
new Ball().puff();
}
public class InnerClass {
//访问内部类
public static void main(String[] args) {
  //创建外部类对象
  Party p = new Party();
  //调用外部类中的puffBall方法
  p.puffBall();
}
}


2.16.3 匿名内部类


定义的匿名内部类有两个含义:


临时定义某一指定类型的子类


定义后即刻创建刚刚定义的这个子类的对象


new 父类或接口(){
  //进行方法重写
};


//已经存在的父类:
public abstract class Person{
  public abstract void eat();
}
//定义并创建该父类的子类对象,并用多态的方式赋值给父类引用变量
Person  p = new Person(){
  public void eat() {
    System.out.println(“eating.......”);
}
};
//调用eat方法
p.eat();


使用匿名对象的方式,将定义子类与创建子类对象两个步骤一次完成。


匿名内部类如果不定义变量引用,则也是匿名对象。


new Pers
   on(){
  public void eat() {
    System.out.println(“eating.......”);
}
}.eat();
目录
相关文章
|
设计模式 Java C#
浅谈面向对象
浅谈面向对象
|
2月前
|
Java
面向对象
面向对象
35 7
|
6月前
|
C++
c++面向对象
c++面向对象
39 0
|
6月前
|
Java 编译器
面向对象篇
面向对象篇
|
Java
1.7 面向对象
1.7 面向对象
53 0
再次认识面向对象
再次认识面向对象
49 0
到底什么是面向对象。
到底什么是面向对象。
43 0
|
Java 编译器
初识面向对象上
初识面向对象上
124 0
初识面向对象上
|
Java
面向对象(二)
javase内容
75 0
|
Java
面向对象(一)
面向对象基础
95 0