Skip to main content

类与对象

类简介

tip

每次创建一个新的类就是一个新的类型,可以作为新的类型进行相应的操作

类就是一个模板,类似C++里的结构体,可以在需要把某一个具有共同特点的事物概括成一个类别。

类跟结构一样也有成员,成员可以是变量、函数、类(内部类)。对于类而言,所有的变量、函数、类(内部类)在类里面都可以是调用的,整个类里都有效,通过this关键字表示当前这个类本身,this关键字主要是为了消除类里的模糊。

class Person {
int id;
int name;
int age;

void say(String content) {...}
void cry(String content) {...}

class wealth {
int count;
int house;

void sell(int id) {...}
void buy(int money) { this.count++; }
}
}

类实例化

类在定义之后内存还没有分配任何空间,只是单纯的声明,要想使用这个类需要以这个类为模板创建对象,也就是将类实例化,生成的变量就是对象,也就是实例。

Person p = new Person();
tip

设计一个类要分析问题涉及哪些属性,涉及什么行为.

方法

方法其实就是类里面的函数,但因为这个函数能够被外部所调用,通过instance.method()的方式,能够对这个类或者其他变量进行操作,表现出一些行为,所以叫做方法。方法分为两种:

  • 普通方法 正常能够被调用的方法,通过下面的方式进行定义:

      public void say() {...}
  • 构造方法 与普通方法不同,构造方法会在类被实例化的时候首先被调用,相当于一个初始化的作用,构造方法没有任何返回值,所以默认是void类型,但这个类型可以不写。构造方法也决定在类被实例化的时候能够传入哪些参数。构造方法名字需要跟类名一致!!。注意:构造方法默认是没有的,如果一个类没有写构造方法那么在编译过程中会自动生成一个空方法,如果自己定义构造方法就不会自己生成空方法.

    class Person {
    int id;
    Person() { System.out.println("111"); }
    Person(int id) { this.id = id; }
    }

    一个类可以有多个构造方法,也就是方法的重载,后面会介绍,根据传入的参数数量、类型不同进行重载。

  • 静态方法

tip

加入了static关键字之后,无论类是否有被实例化,在运行的时候系统会自动分配一段额外的固定的内存空间并进行初始化。每当有新的对象创建的时候这个对应被static修饰的属性或法就会被指向同一段提前分配好的内存空间,一般来说只有在创建对象的时候才会分配内存空间。

与普通方法不同,是加上static关键字的方法,加上static关键字的方法之后就不需要通过对象来调用该方法,直接可以通过类名调用.也可以理解为静态方法对所有的类都是共享(static表示修饰这个属性或方法的都是能够对所有对象共享的)

class Person {
int id;
Person() {}
public static void say() {...}
}

Person.say();

继承

继承是指子类和父类之间的关系,如果子类继承父类,那么子类就能够获得父类的属性属性和方法,不需要再次声明、定义。但是子类要想使用父类的属性、方法等成员需要通过super关键字进行调用。如果将super作为方法来调用则变成调用父类的构造方法.

继承使用extends关键字进行继承,后面跟着被继承的类,也就是父类.

class Person {
int age = 13;
String name;
Person(String name, int age ) {
this.name = name;
this.age = age;
}
void sayAge() { System.out.println(this.age); }
}

class Student extends Person {
int stdId;
Student(int stdId) {
super("AA", 14);
this.stdId = stdId;
}
void sayAge() { System.out.println(super.age); }
void sayAgeParent() { super.sayAge(); }
}

一般来说,所有类的属性成员不应直接暴露在外部,故类的属性成员一般都用private关键字进行修饰,而是通过设置setter,getter方法来对成员变量进行操作。

多态

多态一般分为两种:方法重载、方法重写

方法重载

在同一个类中,定义多个方法名字相同的方法,这些方法功能相同,但是传入的参数不同。当且仅当方法的参数个数、类型、顺序不同才可以构成重载,其他情况一律不构成方法重载,一般都会报错。

class Person {
public void newStudent() { System.out.println("Stu1"); }
//参数个数不同
public void newStudent(String name) { System.out.println(name); }
public void newStudent(String name, int age) { System.out.println(name + age); }
public int newStudent(String name, int age, int count) { System.out.println("StuN"); }
//参数类型顺序不同
public void newStudent(int age, String name) { System.out.println(age + name); }
//参数类型不同
public void newStudent(int age) { System.out.println(age); }
}

方法重写

:::tip java不支持多继承,一个类只能继承一个父类,但是接口可以多继承. :::

方法重写是在只有继承中才能使用,也就是子类与父类之间方法的关系,子类中如果想对父类的方法进行重写。也就是可以重新对父类有的方法进行重写。

重写的方法函数名、返回值类型和形参必须跟父类的一致,只是行为可以不同。

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 类的方法
}
}

上/下转型对象

定义类

class Father {
void say() {
System.out.println("I'm father.");
}
}
class Son extends Father {
public void say() {
System.out.println("I'm son.");
}

public void cry() {
System.out.println("I'm crying.");
}
}
  • 上转型对象 简单理解就是子类对象的类型可以转换成父类对象的类型。上是指父类的类型

    // 这个son变量是一个上转型对象
    IFather son = new Son();
    // 他现在是Father类型,Fahter里有say()方法,所以可以用
    son.say();
    // IFather这个接口里面有cay()方法吗?
    // 没有!所以啊,如果不强制转化的话,不可能用cry()方法
    ((Son) son).cry();
  • 下转型对象 简单理解就是父类对象可以通过强制类型转化转换成子类对象。下是指子类类型.

    // ... 同样是上面的Father和Son 代码略
    Father father = new Father();
    // 此时father成了Son类
    // 编译器为了使用Son类的新特性cry(),必须强转类型,不转会报错
    Son faker = (Son) father;
    faker.cry();

多态实现

tip

多态其实就是相同的方法不同的实现,类型能够灵活转换,不用受限于类型,使得代码更加灵活,因为行为方法已经定义好了,所以只需用通过同一个子类经过上转型之后得到的父类对象就可以使用不同子类里的方法.

tip

凡是跟继承和接口实现相关的都可以实现多态,也就是上转型对象,举个例子:接口变量不能被实例化,但是能够给子类实例化,也就是IAnimal a = new Cat();

  • 多态在java中表现出来的形式是父类根据当前引用的子类对象,根据不同子类对象的特性以不同方式执行相应的行为。

  • 多态的意义

    能够消除类型之间的耦合,有良好的可替换性、可扩充性、接口性、灵活性、简化性。

  • 多态存在条件

    必须有继承、方法重写,父类引用指向子类对象Parent p = new Child();

  • 实现多态的方法:

    1. 子类重写父类的方法
    2. 将子类类型进行转型,转型为父类型(上转型对象)
    3. 根据实际创建的对象决定使用哪个方法。
    class Shape {
    void draw() {}
    }
    class Circle extends Shape {
    void draw() { System.out.println("Circle.draw()"); }
    }
    class Square extends Shape {
    void draw() { System.out.println("Square.draw()"); }
    }
    class Triangle extends Shape {
    void draw() { System.out.println("Triangle.draw()"); }
    }

    一般来说,不会使用上述方法实现多态,一般结合接口实现,因为接口定义之后不需要实现方法,只有根据具体实现这个接口的子类去实现接口的方法。一般实现多态就有三种方法:继承(方法重写),接口,抽象类和抽象方法。

抽象类与接口

抽象类

抽象类可以里积额为不是真实的类,是属于对于类的抽象,也就是能够描述更高一层次的东西,描述各个类之间本质上共同的特点。

tip

抽象类无法被实例化,只能通过继承来实现。

抽象类中的方法为抽象方法,抽象方法需要在子类中重写,普通方法可以重写或者继承。也就是在抽象类当中抽象方法一定要实现,普通方法可以实现可以不实现。同时,抽象方法只能在抽象类中声明.

abstract class Shape {
abstract int getArea(int x, int y);
}

class Circle extends Shape {...}

接口

接口可以理解为完全抽象的抽象类。接口本质不是类,但编写方法跟类很相似,但属于不筒概念,类描述对象的属性和方法,而接口是描述子类属性和要实现的方法。

接口无法被实例化,只能通过继承来实现接口,也就是一定会出现子类,要实现一个接口的类就必须把接口当中所有的方法都要实现,否则需要声明为抽象类。(但接口变量能够承接其子类对象,也就是进行上转型)

tip

接口其实类似于一个子类相互交流的桥梁,你给我一个接口,我来实现,实现之后你也能用。

  • 接口与类的区别

    1. 接口不能用于实例化对象。
    2. 接口没有构造方法。
    3. 接口中所有的方法必须是抽象方法。
    4. 接口不能包含成员变量,除了 static 和 final 变量。
    5. 接口不是被类继承了,而是要被类实现。
    6. 接口支持多继承。
  • 接口特性

    1. 接口中每一个方法也是隐式抽象的,接口中的方法会被隐式的指定为public abstract(只能是 public abstract,其他修饰符都会报错)。
    2. 接口中可以含有变量,但是接口中的变量会被隐式的指定为 public static final 变量(并且只能是 public,用 private 修饰会报编译错误)。
    3. 接口中的方法是不能在接口中实现的,只能由实现接口的类来实现接口中的方法。
  • 抽象类和接口的区别

    1. 抽象类中的方法可以有方法体,就是能实现方法的具体功能,但是接口中的方法不行。
    2. 抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是 public static final 类型的。
    3. 接口中不能含有静态代码块以及静态方法(用 static 修饰的方法),而抽象类是可以有静态代码块和静态方法。
    4. 一个类只能继承一个抽象类,而一个类却可以实现多个接口。
interface IAnimal {
String name;
public void say();
public void play();
}
class Cat implements IAnimal {
Cat(String name) { super.name = name; }
void say() { System.out.println("cat"); }
void play() { System.out.println("play"); }
}

IAnimal a = new Cat("XXX");
Cat c = new Cat("x");