OOP(面向对象编程)基本特征:继承,封装,多态,组合…(千万不要在简历上直接写OOP三大特征,因为OOP并不只是具有继承,封装,多态这些特征)
背景
代码中创建的类,主要是为了抽象现实中的一些事物(包含属性和方法),有的时候客观事物之间就存在一些关联关系,那么在表示成类和对象的时候也会存在一定的关联。如下:
class Animal {
public String name;
public Animal(String name) {
this.name = name;
}
public void eat(){
System.out.println(this.name+"Animal::eat()");
}
public void sleep(){
System.out.println(this.name+"Animal::sleep()");
}
}
class Cat {
public String name;
public Cat(String name) {
this.name = name;
}
public void eat(){
System.out.println(this.name+"Cat::eat()");
}
}
class Bird {
public String name;
public Bird(String name) {
this.name = name;
}
public void eat(){
System.out.println(this.name+"Bird::eat()");
}
public void fly(){
System.out.println(this.name+"Bird::fly()");
}
}
public class TestDemo {
public static void main(String[] args) {
}
}
这个代码我们发现其中存在了大量的冗余代码。我们发现Animal和Cat以及Bird这几个类中存在一定的关联关系:
- 这三个类都具备一个相同的eat方法,而且行为是完全一样的。
- 这三个类都具备一个相同的name属性,而且意义是完全一样的。
- 从逻辑上讲,Cat 和Bird都是一种Animal(满足…is a …语义)
此时,就可以让Cat和Bird分别继承Animal类,来达到代码重用的效果。
继承的优点:可以达到代码复用的效果。 面试的一个问题:请问子类继承了父类的什么? 答:继承了父类除构造方法外所有的东西 继承之后代码:
class Animal {
public String name;
public void eat(){
System.out.println(this.name+"Animal::eat()");
}
public void sleep(){
System.out.println(this.name+"Animal::sleep()");
}
}
class Cat extends Animal{
}
class Bird extends Animal{
public void fly(){
System.out.println(this.name+"Bird::fly()");
}
}
public class TestDemo {
public static void main(String[] args) {
Cat cat = new Cat();
Bird bird = new Bird();
cat.name = "miaomiao";
cat.eat();
cat.sleep();
bird.name = "bird";
bird.eat();
bird.fly();
}
}
这段代码说明把cat和bird中重复的属性和方法注释掉,cat和bird中继承了Animal中的属性和方法。 以下示例也可以说明,派生类继承了基类的属性。
class Base {
public int a;
}
class Derive extends Base {
public int b;
}
public class TestDemo2 {
public static void main(String[] args) {
Derive derive = new Derive();
derive.a = 99;
System.out.println(derive.a);
}
}
语法规则
- 使用extends指定父类
- Java中一个子类只能继承一个父类(而C++/Python等语言支持多继承)
- 子类会继承父类除构造方法外所有的东西
- 对于父类的private的字段和方法,子类中是无法访问的,但是也继承了
- 子类的实例中,也包含着父类的实例,可以使用super关键字得到父类实例的引用。
.
class Animal {
public String name;
public Animal(String name) {
this.name = name;
System.out.println(name+"animal");
}
public void eat(){
System.out.println(this.name+"Animal::eat()");
}
public void sleep(){
System.out.println(this.name+"Animal::sleep()");
}
}
class Cat extends Animal{
public Cat(String name){
super(name);
super.name = "nihao";
super.sleep();
}
}
class Bird extends Animal{
public Bird(String name) {
super(name);
this.name = name;
}
public void fly(){
System.out.println(this.name+"Bird::fly()");
}
}
public class TestDemo {
public static void main(String[] args) {
Cat cat = new Cat("mimi");
}
}
子类在构造的时候要先帮助父类进行构造。
super关键字和this关键字的区别:
this:代表当前对象的引用
- this();调用本类的构造方法
- this.data;访问本类中的属性
- this.func();调用本类中的成员方法
super:代表父类对象的引用
- super();调用父类的构造方法
- super.data();访问父类的属性
- super.func();访问父类的成员方法
protected关键字
我们发现,如果把字段设为private,那么子类不能访问。但是设成public ,又违背了我们“封装”的初衷。 两全其美的方法就是protected关键字:
- 对于类的调用者来说,protected修饰的字段和方法是不能访问的。
- 对于不同包中的类的子类和同一个包中的其他类来说,protected修饰的字段和方法是可以访问的。
访问权限限定符
我们希望类要尽量做到“封装”,即隐藏内部实现细节,只暴露出必要的信息给类的调用者。 因此我们在使用的时候应该尽可能的使用比较严格的访问权限,例如:如果一个方法能用private,就尽量不要用public 。 另外,还有一种简单粗暴的做法:将所有的字段设为private,将所有的方法设为public。不过,这种方法是对访问权限的滥用,还是应该在写代码的时候认真思考,该类提供的字段方法到底给“谁”使用(是类内部自己用,还是其他的类调用使用,还是不同包的子类使用)
class Animal {
public String name;
public Animal(String name){
this.name = name;
}
public void eat(String food){
System.out.println(this.name + "正在吃"+ food);
}
}
class Cat extends Animal {
public Cat(String name){
super(name);
}
}
class Bird extends Animal {
public Bird(String name){
super(name);
}
public void fly(){
System.out.println(this.name+"正在飞");
}
}
public class Test {
public static void main(String[] args) {
Cat cat = new Cat("小黑");
cat.eat("猫粮");
Bird bird = new Bird("圆圆");
bird.fly();
}
}
输出为:
|