Java基础笔记(二)---类、对象、接口和继承

(1)类和对象

(1.1)创建对象

(1)Hero h = new Hero();
创建过程中JVM的对应处理:

  • Hero h创建对象对应在内存中开辟栈内存,用来存放对象;
  • new Hero()实例化对象对应在内存中开辟堆内存,并且把类的非静态属性方法等放进去;
  • =就是让栈内存的引用指向堆内存

(2)Java有5种方式创建对象

  1. 使用 new 关键字(最常用): ObjectName obj = new ObjectName();
  2. 使用反射的Class类的newInstance()方法: ObjectName obj = ObjectName.class.newInstance();
  3. 使用反射的Constructor类的newInstance()方法: ObjectName obj = ObjectName.class.getConstructor.newInstance();
  4. 使用对象克隆clone()方法: ObjectName obj = obj.clone();
  5. 使用反序列化(ObjectInputStream)的readObject()方法: try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream(FILE_NAME))) { ObjectName obj = ois.readObject(); }

(1.2)访问修饰符

(1)private 私有的

自身:是可以访问的
同包子类:不能继承
不同包子类:不能继承
同包类:不能访问
其他包类:不能访问

(2)package/friendly/default 不写
在这里插入图片描述
(3)protected 受保护的
在这里插入图片描述
(4)public 公共的
在这里插入图片描述总结

  1. 属性通常使用private封装起来
  2. 方法一般使用public用于被调用
  3. 会被子类继承的方法,通常使用protected
  4. package用的不多
    在这里插入图片描述

(1.3)类属性(静态属性static)

当一个属性被static修饰的时候,就叫做类属性,又叫做静态属性。如果一个属性声明成类属性,那么所有的对象,都共享这么一个值 。

例如:给英雄设置一个类属性叫做“版权" (copyright), 无论有多少个具体的英雄,所有的英雄的版权都属于 Riot Games公司。

如果一个属性,每个英雄都不一样,比如name,这样的属性就应该设计为对象属性,因为它是跟着对象走的,每个对象的name都是不同的。

如果一个属性,所有的英雄都共享,都是一样的,那么就应该设计为类属性。比如血量上限,所有的英雄的血量上限都是 9999,不会因为英雄不同,而取不同的值。 这样的属性,就适合设计为类属性

(1.4)类方法(静态方法static)

又叫做静态方法,访问一个对象方法,必须建立在有一个对象的前提的基础上。而访问类方法,不需要对象的存在,直接就访问。

静态方法static不能调用对象的变量,因为静态方法在类加载的时候就初始化了,对象变量需要在新建对象后才能使用。也就是说不能再静态方法中引用非静态的域。

public class Test{
	private float f=1.0f;
	int n=12;
	static int n=1;
	public static void main(String args[]){
		Test t=new Test();
		//静态方法引用静态的变量,其他变量不能引用
		Test.n;
	}
}

例如:常用的main方法,不需要有对象,可以直接使用,属于静态方法

如果方法里访问了对象属性,那么这个方法,就必须设计为对象方法。如果一个方法,没有调用任何对象属性,那么就可以考虑设计为类方法。

(2)接口与继承

(2.1)对象转型

(1)子类转父类(一定可以)
Hero h = new ADHero();
(2)父类转子类(要进行强转)
ADHero ad = new Hero();

(2.2)继承

(2.2.1)简单介绍继承

(1)java中只允许单继承,也就是说一个子类只能继承一个父类。但是允许多重继承,例如一个子类有一个父类,它的父类还可以有自己的父类。
1-为什么需要继承?企鹅和老鼠都有eat、run等方法,这些代码是重复的,后期维护起来就改动两份一样的代码。要解决这个问题,就可以把同类型类的相同方法向上提取到父类里,后期维护的时候只需要修改父类里的方法就可以了。
2-为什么有了抽象类可以继承以后,又出现了更加抽象的接口,抽象类只允许单继承,一个类却可以实现多个接口,更加灵活。并且继承会提高类之间的耦合性,导致代码之间的联系更加紧密,代码独立性变差

(2)子类通过继承,可以直接调用父类中public的属性和方法。但是构造函数不能被继承,构造方法只能被显式或隐式的调用。

子类可以拥有自己的属性和方法,即子类可以对父类进行扩展。

子类可以用自己的方式实现父类的方法。

(3)子类是不继承父类的构造器的,它只是调用(隐式或显式)。子类对象实例化的时候,会默认先调用父类的构造器,然后才会调用自身的构造器。

如果父类的构造器带有参数,则必须在子类的构造器中显式地通过 super 关键字调用父类的构造器并配以适当的参数列表。如果父类构造器没有参数,则在子类的构造器中不需要使用 super 关键字调用父类构造器,系统会自动调用父类的无参构造器。

例如在子类构造器中有一个super()方法,就是在在调用父类的构造器,不过不写也没事,会默认调用。

class SuperClass {
  private int n;
  SuperClass(){
    System.out.println("SuperClass()");
  }
  SuperClass(int n) {
    System.out.println("SuperClass(int n)");
    this.n = n;
  }
}
// SubClass 类继承
class SubClass extends SuperClass{
  private int n;
  
  SubClass(){ // 自动调用父类的无参数构造器
    System.out.println("SubClass");
  }  
  
  public SubClass(int n){ 
    super(300);  // 调用父类中带有参数的构造器
    System.out.println("SubClass(int n):"+n);
    this.n = n;
  }
}

super关键字:我们可以通过super关键字来实现对父类成员的访问,用来引用当前对象的父类。

this关键字:指向自己的引用。

class Animal {
  void eat() {
    System.out.println("animal : eat");
  }
}
 
class Dog extends Animal {
  void eat() {
    System.out.println("dog : eat");
  }
  void eatTest() {
    this.eat();   // this 调用自己的方法
    super.eat();  // super 调用父类方法
  }
}

(4)final关键字
final 可以用来修饰变量(包括类属性、对象属性、局部变量和形参)、方法(包括类方法和对象方法)和类。
使用 final 关键字声明类,就是把类定义定义为最终类,不能被继承,或者用于修饰方法,该方法不能被子类重写:
final 定义的类,其中的属性、方法不是 final 的。

(5)构造器

(2.2.2)方法的覆写(继承:重写override,体现方法的加强性)

重写是子类对父类的允许访问的方法的实现过程进行重新编写, 返回值和形参都不能改变。即外壳不变,核心重写!又叫覆盖 override。

方法的重写规则

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

重载(overloading) 是在一个类里面,方法名字相同,而参数不同。返回类型可以相同也可以不同。每个重载的方法(或者构造函数)都必须有一个独一无二的参数类型列表。最常用的地方就是构造器的重载。
方法重载的规则

  • 被重载的方法必须改变参数列表(参数个数或类型或顺序不一样);
  • 对返回值没有要求。如果参数的个数、类型、次序都相同,只有返回值不相同,无法构成重载。
  • 被重载的方法可以改变返回类型;
  • 被重载的方法可以改变访问修饰符;与权限修饰符也无关。
  • 被重载的方法可以声明新的或更广的检查异常;
  • 方法能够在同一个类中或者在一个子类中被重载。

调用重载的方法时通过传递给她们不同的参数个数、参数类型、次序来决定具体使用哪个方法,这叫多态。

(2.2.4)super关键字

(1)只要是被子类重写的方法,不被super调用都是调用子类方法
向上转型:父类只能调用父类方法或者子类覆写后的方法,而子类中的单独方法则是无法调用

public class Base
{
   public void methodOne()
   {
      System.out.print("A");
      //没用super调用,所以子类对象b调用的是子类的方法,输出的是:BD
      methodTwo();
   }
 
   public void methodTwo()
   {
      System.out.print("B");
   }
}
 
public class Derived extends Base
{
   public void methodOne()
   {
      super.methodOne();
      System.out.print("C");
   }
 
   public void methodTwo()
   {
      super.methodTwo();
      System.out.print("D");
   }
}

Base b = new Derived(); 调用执行b.methodOne()后,输出结果是:ABDC

继承关系中,用多态创建子类对象,我们在调用一个父类被子类重写的方法时,不管是在子类中调用还是在父类中调用,不被super调用都是调用子类方法,只有用super调用时才是调用父类的方法。

(2.3)重写和重载的区别

请添加图片描述
方法的重写(Overriding)和重载(Overloading)是java多态性的不同表现,重写是父类与子类之间多态性的一种表现,重载可以理解成多态的具体表现形式。
(1)方法重载是一个类中定义了多个方法名相同,而他们的参数的数量不同或数量相同而类型和次序不同,则称为方法的重载(Overloading)。
(2)方法重写是在子类存在方法与父类的方法的名字相同,而且参数的个数与类型一样,返回值也一样的方法,就称为重写(Overriding)。
(3)方法重载是一个类的多态性表现,而方法重写是子类与父类的一种多态性表现。

请添加图片描述

(2.4)多态

(1)多态的种类
1-操作符的多态 :+ 可以作为算数运算,也可以作为字符串连接
2-类的多态 :父类引用指向子类对象(对象转型)

(2)要实现类的多态,需要如下条件:
1-父类(接口)引用指向子类对象:Parent p = new Child();
2-调用的方法有继承和重写

(3)多态的案例一
有时候想把一个对象不当做它所属的特定类型来对待,而是把它当做其父类的对象来对待。例如“把自行车看作是交通工具”。
但是在这种【向上转型】的时候如何确定是什么类型的子类转上来的呢,用到一个概念【后期绑定】,就是说当向对象发送消息时,被调用的diamante直到运行的时候才能确定。
请添加图片描述

void doSomething (Shape shape) {
    shape.erase();
    shape.draw();
}

在调用这个方法的时候,可以把子类对象当做参数
Circle circle = new Circle();
Line line = new Line();
doSomething (circle);
doSomething (line);

(4)多态的案例二
当使用多态方式调用方法时,首先检查父类中是否有该方法,如果没有,则编译错误;如果有,再去调用子类的同名方法。
多态的好处:可以使程序有良好的扩展,并可以对所有类的对象进行通用处理。


public class Test {
    public static void main(String[] args) {
      show(new Cat());  // 以 Cat 对象调用 show 方法
      show(new Dog());  // 以 Dog 对象调用 show 方法
                
      Animal a = new Cat();  // 向上转型  
      a.eat();               // 调用的是 Cat 的 eat
      Cat c = (Cat)a;        // 向下转型  
      c.work();        // 调用的是 Cat 的 work
   }  
            
   public static void show(Animal a)  {
      a.eat();  
      // 类型判断
      if (a instanceof Cat)  {  // 猫做的事情 
          Cat c = (Cat)a;  //向下转型
          c.work();  
      } else if (a instanceof Dog) { // 狗做的事情 
          Dog c = (Dog)a;  //向下转型
          c.work();  
      }  
   }  
}
 
abstract class Animal {  
    abstract void eat();  
}  
  
class Cat extends Animal {  
    public void eat() {  
        System.out.println("吃鱼");  
    }  
    public void work() {  
        System.out.println("抓老鼠");  
    }  
}  
  
class Dog extends Animal {  
    public void eat() {  
        System.out.println("吃骨头");  
    }  
    public void work() {  
        System.out.println("看家");  
    }  
}

吃鱼
抓老鼠
吃骨头
看家
吃鱼
抓老鼠

(5)多态的案例三

public class Employee {
   private String name;
   private String address;
   private int number;
   public Employee(String name, String address, int number) {
      System.out.println("Employee 构造函数");
      this.name = name;
      this.address = address;
      this.number = number;
   }
   public void mailCheck() {
      System.out.println("邮寄支票给: " + this.name
       + " " + this.address);
   }
}

public class Salary extends Employee
{
   private double salary; // 全年工资
   public Salary(String name, String address, int number, double salary) {
       super(name, address, number);
       setSalary(salary);
   }
   public void mailCheck() {
       System.out.println("Salary 类的 mailCheck 方法 ");
       System.out.println("邮寄支票给:" + getName()
       + " ,工资为:" + salary);
   }
}

public class VirtualDemo {
   public static void main(String [] args) {
      Salary s = new Salary("员工 A", "北京", 3, 3600.00);
      Employee e = new Salary("员工 B", "上海", 2, 2400.00);
      System.out.println("使用 Salary 的引用调用 mailCheck -- ");
      s.mailCheck();
      System.out.println("\n使用 Employee 的引用调用 mailCheck--");
      e.mailCheck();
    }
}

Employee 构造函数
Employee 构造函数
使用 Salary 的引用调用 mailCheck –
Salary 类的 mailCheck 方法
邮寄支票给:员工 A ,工资为:3600.0
使用 Employee 的引用调用 mailCheck–
Salary 类的 mailCheck 方法
邮寄支票给:员工 B ,工资为:2400.0

即使向上转型了,调用的还是子类重写的方法。要想调用父类中被重写的方法,则必须使用关键字 super。

容器设定存储对象的类型,这样可以避免转型的危险和开销。

ArrayList<Shape> shapes = new ArrayList<Shape>();

(2.4)抽象类和接口

(2.4.1)抽象类

(1)抽象类介绍
抽象类的作用类似“模板”,目的是让设计者依据它的格式来修改并创建新的类。但是并不能直接由抽象类创建对象,只能通过子类来创建对象。
规则如下:

  • 当一个类有抽象方法的时候,该类必须被声明为抽象类。抽象类不一定必须有抽象方法【有抽象方法的必是抽象类,抽象类不一定必有抽象方法】
  • 抽象类可以有构造方法,只不过不能new实例化抽象类对象,所以说抽象类只能声明对象,不能实例化对象
  • 抽象类和抽象方法都必须使用abstract关键字声明
  • 抽象方法只需要声明,而不需要实现
  • 抽象类必须被子继承,子类(如果不是抽象类)必须覆写抽象类中的全部抽象方法【抽象类的子类没得选,接口的实现类有的选】
  • 抽象类不能使用final关键字声明,因为final修饰的类不能被继承,也不能使用private声明

抽象类除了不能实例化对象之外,类的其它功能依然存在,成员变量、成员方法和构造方法的访问方式和普通类一样。

由于抽象类不能实例化对象,所以抽象类必须被继承,才能被使用。也是因为这个原因,通常在设计阶段决定要不要设计抽象类。

一个类只能继承一个抽象类,而一个类却可以实现多个接口。

public abstract class Employee {}

(2)抽象方法介绍
Abstract 关键字同样可以用来声明抽象方法,抽象方法只包含一个方法名,而没有方法体。抽象方法没有定义,方法名后面直接跟一个分号,而不是花括号。

public abstract double computePay();

(3)抽象类总结

  1. 抽象类不能被实例化,如果被实例化,就会报错,编译无法通过。只有抽象类的非抽象子类可以创建对象。
  2. 抽象类中不一定包含抽象方法,但是有抽象方法的类必定是抽象类。
  3. 抽象类中的抽象方法只是声明,不包含方法体,就是不给出方法的具体实现也就是方法的具体功能。
  4. 构造方法,类方法(用 static 修饰的方法)不能声明为抽象方法。
  5. 抽象类的子类必须给出抽象类中的抽象方法的具体实现,除非该子类也是抽象类。
(2.4.2)接口

(1)接口的介绍
抽象类可以实现轻量化,但是只能继承,且只能单继承,并且抽象类里还是可以有具体方法的,还不够完全抽象,继承的重写还会使代码之间的关系更加紧密,提高耦合度。而接口可以打破单继承的局限性,可以一个类实现多个接口,并且所有的方法都是抽象的,更加的轻量,更加的灵活,使得代码之间的关系没有那么紧密了,实现解耦。

  1. 接口可以理解成一种特殊的抽象类,由全局常量和public的抽象方法组成,是一种用来解决多继承局限的手段。接口中的方法必须定义为public。
  2. 一个子类可以实现多个接口,摆脱了抽象类的单继承局限
  3. 接口中的所有方法都必须是抽象的。接口中的方法定义默认为public abstract类型,接口中的成员变量类型默认为public static final。
  4. 除非实现接口的类是抽象类,否则该类要定义接口中的所有方法。

单继承和多实现的例子:

public class HashMap<K, V> extends AbstractMap<K, V> implements Map<K, V>, Cloneable, Serializable

(2)抽象类和接口的区别
类继承可以实现类的复用和拓展
有时候确定一个类型的父类,父类中的方法设计成抽象类,只有定义但没有方法体,具体的实现放在子类继承的时候再做,这样可以节省很多的代码量,使得父类更轻量
但是抽象类还是不够抽象,还是可以有很多有方法体的方法,我们想让父类更加的轻量,绝对的抽象,那就有了接口,接口的好处就是没有方法实现,更加自由的调用和组合使用

  1. 子类只能继承一个抽象类,不能继承多个;子类可以实现多个接口
  2. 抽象类和接口中都可以包含静态成员变量。抽象类可以定义public,protected,package,private,静态和非静态属性,final和非final属性;但是接口中声明的属性,只能是public、静态、final的【抽象类中可以有普通成员变量,接口中没有普通成员变量】
  3. 抽象类中可以包含静态方法,接口中不能包含静态方法
  4. 抽象类可以有构造方法,接口中不能有构造方法。
  5. 抽象类中可以有普通成员变量,接口中没有普通成员变量
  6. 抽象类中可以包含非抽象的普通方法,接口中的所有方法必须都是抽象的,不能有非抽象的普通方法。
  7. 优先选用接口,尽量少用抽象类
  8. 抽象类和接口都不能被实例化。但是可以创建对象,向上转型,让其子类或者实现类实例化
  9. 从设计层面来说,抽象是对类的抽象,是一种模板设计,而接口是对行为的抽象,是一种行为的规范

(3)接口的继承
一个接口能继承另一个接口,和类之间的继承方式比较相似。接口的继承使用extends关键字,子接口继承父接口的方法。

public interface Sports
{
   public void setHomeTeam(String name);
   public void setVisitingTeam(String name);
}
 
// 文件名: Football.java
public interface Football extends Sports
{
   public void homeTeamScored(int points);
   public void visitingTeamScored(int points);
   public void endOfQuarter(int quarter);
}
 
// 文件名: Hockey.java
public interface Hockey extends Sports
{
   public void homeGoalScored();
   public void visitingGoalScored();
   public void endOfPeriod(int period);
   public void overtimePeriod(int ot);
}

Hockey接口自己声明了四个方法,从Sports接口继承了两个方法,这样,实现Hockey接口的类需要实现六个方法。

相似的,实现Football接口的类需要实现五个方法,其中两个来自于Sports接口。

(4)接口的多继承
在Java中,类的多继承是不合法,但接口允许多继承。
在接口的多继承中extends关键字只需要使用一次,在其后跟着继承接口。

public interface Hockey extends Sports, Event
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值