面向对象特征

Copyright @BJTShang

面向对象基础

1.面向对象概述

  • 面向对象三个特征:封装(Encapsulation),继承(Inheritance),多态(Polymorphism)。(都是什么样的表现形式?)
  • 开发,其实就是找对象,建立对象,使用对象,并维护对象之间的关系
  • 类和对象的关系:
    • 类:对事物的描述;属性和行为共同组成了类的成员(成员变量和成员方法,Java中方法就是函数)
    • 对象:java在堆内存中使用new操作符建立的实体;存储对象的属性和行为;

2. 封装

  • set() get()方法: 使得可以访问类内部的私有化(private)成员属性;参考:http://blog.csdn.net/shenqidemao/article/details/50699657
    • 首先:为何要私有化数据?提高安全性,使得外部不能够直接访问,也是封装的一种表现形式;
    • 其次,为何又要设置方法来使用数据(使用数据的两种方式:设置:set(),获取:get())?可以在方法中对数据的使用制定规则(逻辑判断语句)!
    • 从而提高代码的健壮性,安全性。将不需要对外提供的内容(通常是属性)隐藏,提供公共(public)方法对齐访问;

3.构造方法

特点

  • 与类同名;
  • 没有返回值,也就不能写return;
  • 没有定义构造方法时,系统默认加入一个空内容空参数的构造方法;但是,定义了构造方法后,默认的构造方法消失;

作用

  • 对象初始化;

与一般方法的区别

  • 写法不同;
  • 对象建立的同时运行构造方法,初始化对象,而一般方法必须使用对象引用调用,给对象添加初始化以外的功能;
  • 构造函数只在对象建立时运行一次,而一般方法可以多次被调用运行;

构造代码块

  • 和构造方法相同:作用也是初始化对象,对象一建立就运行;但是优先于构造方法运行
  • 与构造方法的区别:对所有对象统一初始化,没有构造方法的重载功能;

4. this关键字

看上去:是用于区分局部变量和成员变量同名的情况;为何可以解决这个问题?

class Person{
    Person(String name){
        this.name = name;
    }
    public static void main(String[] agrs){
        new Person("BJTShang");
    }
}

this 代表本类对象,更具体:代表本方法所属类的对象的引用

this关键字的使用

  • 当定义类中方法时,该方法内部要用到该方法的对象时,用this表示这个对象,例如:对比两个人的年龄,相同返回true,不同返回false;
class Person{
    private int age;
    Person(int age){
        this.age = age;
    }

    public boolean compare(Person p){
        return this.age==p.age;//这里this代表使用这个方法的p1对象引用,局部变量p代表传入的p2对象引用
    }

    public void main(String[] agrs){
        Person p1 = new Person(20);
        Person p2 = new Person(25);
        System.out.println(p1.compare(p2));
    }
}

this的一个特殊应用:构造方法的相互调用,且必须放在构造方法的第一行(防止该构造方法后续的语句修改调用的构造函数中的初始化参数)

class Person{
    String name;
    private age;
    Person(){
    }
    Person(String name){
        this.name = name;
    }
    Person(String name,int age){
        this(name);//相当于p1(name)调用同类的另一个构造方法
        this();//调用空参数的构造方法
        this.age = age;
    }
    public static void main(String agrs){
        Person p1 = new Person("BJTShang",25);
    }
}

另外,this还可以作为方法的返回值使用,典型应用在get()方法中。

5.对象的初始化

Person p1 = new Person("BJTShang",20);

这句对象初始化语句在内存中的过程(不考虑在方法区中的数据):
* 因为new用到了Person.class,所以会找到Person.class文件加载在内存中;
* 如果有的话,执行类中的static{}静态代码块,对类进行初始化;
* 在堆内存中开辟空间,分配内存地址;
* 在对内存中建立对象的特有属性(未被static修饰的,被static修饰的属性放入方法区/共享区/数据区),并进行默认初始化,例如String name=null;int age=0;
* 对属性进行显式初始化;例如在类中已经定义的String name=”BJTShang”;
* 对对象进行{}构造代码块初始化;
* 对对象进行对应的构造方法初始化;
* 将内存地址赋值给栈内存中的引用变量p1;

6.单例设计模式(重要):

一个类在内存中只存在一个对象
* 禁止其他程序建立该类对象;(私有化构造方法)
* 避免其他程序建立对象,在本类中自定义一个对象;(在类中创建一个本类对象)
* 对外提供一些访问方式;(提供一个方法,可以得到该对象)

class Single{
    private Single(){}
    private static Single s= new Single();
    public static Single getInstance(){
        return s;
    }
} 

class SingleSet{
    public static void main(String[] agrs){
        Single s1 = Single.getInstance();
    }
}

7.继承

重载:只看参数列表不一样;
重写:子父类方法一模一样;

子类的实例化过程:

  • 子父类中的构造方法:在对子类对象进行初始化时,父类的构造方法也会运行,原因是构造方法默认第一行有一条隐式的语句super(),调用父类空参数的构造方法;且所有的子类构造方法都有这条语句;
  • 当然子类的构造方法第一行也可以手动指定this语句来访问本类构造方法;子类中至少有一个构造方法会去访问父类构造方法,放在第一行;

final关键字,作为修饰符

继承的优点:提高复用性,引入了多态的概念;
继承的缺点:打破了封装性。

  1. 可以修饰类,方法,变量;
  2. 被final修饰的类不可以被继承;防止其中的方法被复写!
  3. 也可以修饰方法,规定不允许被复写的方法(在有继承类的时候常用);
  4. 也可以修饰变量,使其变成一个常量,既可以修饰全局变量,也可以修饰局部变量—适用于固定值的数据,所有字母都大写,单词间通过下划线连接;例如:final double CONSTANT_PI = 3.14 通常前面还会加上public和static修饰符,使其可被类名调用,并且全局可用;
  5. *内部类定义在类中的局部位置上时,只能访问该局部被final修饰的局部变量;

抽象类(没有子类的抽象类没有意义,why?)

多个类中出现相同功能,功能主体不同(方法声明相同,方法体不同);可以向上抽取功能定义,标识为抽象方法(没有方法体);该抽象方法必须存在抽象类中;

  1. 抽象方法一定抽象在抽象类中,被abstract关键字修饰;但是抽象类中可以包括不抽象的方法;因此,抽象类和一般类没有太大的不同,仅仅是包含了抽象方法;
  2. 抽象类不可以用new创建对象,因为抽象类中的抽象方法没有具体实现;
  3. 抽象类中的方法要被使用,必须由子类复写所有的抽象方法,再建立子类对象;
  4. 抽象类可以强迫子类复写自己的方法!
  5. 特殊:抽象类中可以不定义抽象方法:基本没有意义,仅仅是不让该类建立对象;
  6. *常用于模板方法设计模式(常常使用final,abstract关键字):在定义方法时,功能的一部分是确定的(定义为final防止被复写),另一部分不确定,那么就把不确定的部分暴露出去(abstract方法,或者设定默认的方法),让子类去实现;

8 接口

简单理解为:如果抽象类中的所有方法都是抽象时,可以通过接口的形式来表示(一种特殊的抽象类,比抽象类更抽象!);

接口定义时,格式特点:

  1. 接口中常见定义:常量,抽象方法;
  2. 接口中的常见的成员都有固定修饰符(没有默认被补上);

    1. 常量:public static final
    2. 方法:public abstract
    3. 接口中成员都是public的

      • 什么叫继承?父类中有非抽象内容,可以直接拿过来用;
      • 什么叫接口?首先肯定不可以实例化;更重要的是子类需要将接口中的抽象方法全部(因此必须是public修饰的暴露出去实现,并且复写时,方法也必须是public修饰的)实现后,才能实例化;
interface Inter{
    public static final int NUM = 1;
    public abstract void show();
}

class Test implements Inter{

}

实现接口

  1. Java不可以多类继承,但是支持接口的多实现;
  2. 可以继承一个类,同时实现多个接口;
  3. 接口与接口之间的关系:继承关系,且可以多继承:(注意:多继承的接口中不能定义返回值不同的同名抽象方法!)
interface C extends B,A

举例:

abstract class Student{
    abstract void study();
    void sleep(){
        System.out.println("sleep");
    }
    //不是子类的共性,使用接口来实现abstract void smoke();
}

interface Smoking{
    void smoke();
}

class Zhangsan extends Student implements Smoking{
    void study(){}
    public void smoke(){}
}
/**不属于Student类的teacherA也可以实现Smoking的接口,扩展teacher的功能, 这种Smoking功能,不是所有Teacher类都有的*/
class teacherA extends Teacher implements Smoking{
    ...
    public void smoke(){}
}

9 多态

要有继承,要有重写,要有父类引用指向子类对象;
1. 多态的体现
1. 父类的引用指向了自己的子类对象;
2. 父类的引用也可以接收自己的子类对象(传递参数);
2. 多态的前提
1. 类与类之间需要有关系:继承,实现;
2. 存在方法覆盖,需要使用这个覆盖的方法;
3. 多态的好处
1. 提高了程序的扩展性;
4. 多态的应用

转型

  • 向上转型
  • 向下转型
class Cat extends Animal{
...
}
Animal a = new Cat();//向上转型
a.eat();
//a.catchMouse();--> 将父类引用转换成子类类型
Cat c = (Cat)a;//向下转型
c.catchMouse();

在多态中,成员方法的特点:
1. 在编译时期:引用所属的类中是否有调用的方法:如果有,编译通过,如果没有(通常会错误使用子类中的方法),编译失败;
2. 在运行时期:引用指向子类创建的对象,通过该对象的引用访问方法,因此,如果父方法中的方法被子方法重写,再使用父类引用调用此方法,将执行的是子类中重新定义的方法;
3. (面试)但是如果父类的方法是static的,则没有此特性,因为只有非静态的方法才可以被重写:在类初始化时,静态方法就已经存在于方法区的静态区(内存)中了,也就是说该方法已经被该类静态绑定,相对的是非静态方法被(例如this关键字)动态绑定

class Father{
    void method1(){//没有返回值的通常叫做工具类方法
    }
    void method2(){
    }
}

class Son extends Father{
    void method1(){//重写父类中的method1()方法
    }
    void method3(){
    }

    public static void main(String[] agrs){
        Father f = new Son();
        f.method1();//执行的是被子类重写的方法
        f.method2();
        //f.method3();编译无法通过!因为在编译时,不创建对象,此时引用型变量f不指向子类对象,当然无法使用子类中才有的方法!
    }
}

(面试)在多态中,成员变量的特点:(与方法不同!因为子类变量不是重写父类变量,而是在实例化时,父类和子类的同名变量同时存在于堆内存中?
* 无论编译还是运行,都参考引用变量所属的类中的成员变量;

原则:静态参考类去确定:静态绑定;非静态参考对象去确定:动态绑定!

10 Object 类(所有类具备的功能)

  1. boolean equals(Object obj){}
  2. String toString(){} 通常会重写
  3. Class getClass(){}

11 内部类

为何要有内部类?要想访问外部类成员,定义方法不就行了么?继承不也可以么?为啥要有内部类?

观点:内部类“实现”了Java多重继承?每个内部类都可以独立继承一个接口(或者具体的类)来实现;

  1. 作用:可以直接访问外部类的成员(甚至是private修饰的)
    • 为什么可以直接访问?内部类中持有了一个外部类的引用;格式:外部类名.this
  2. 而外部类要访问内部类则必须建立内部类对象
    • Outer.Inner in = new Outer().new Inner();
  3. 所有的类都不能被private修饰:只有内部类(此时处于外部类的成员位置上)可以,将其在外部类中进行封装;
  4. 也可以被static修饰,此时只能访问外部类的静态成员;
    1. 其他外部类中,访问外部类静态内部类中的非静态成员:new Outer.Inner().method();
    2. 其他外部类中,访问外部类静态内部类中的静态方法:Outer.Inner.method();
    3. 内部类中定义了静态成员,则该内部类也必须是静态的;反之不一定;

局部内部类

不能被private和static修饰,且必须在局部先创建此类对象,才能使用:
* 成员内部类才可以被private,static修饰;
* 局部内部类不可以被private,static修饰,因为private和static都是成员修饰符!;
* 局部内部类可以直接访问外部类的成员,因为它持有一个外部类的引用;但是不可以访问它所在局部中的变量,只能访问被final修饰的局部变量:

class Outer{
    int x = 3;
    void outermethod(){
        //int y = 4;局部内部类需要访问局部变量,需要声明为final
        final y = 4;
        class Inner{
            void inner method(){
                System.out.println(x+" "+y);//x前省略Outer.this.x
            }
        }
    }
}

匿名内部类(就是内部类的简写形式)

内部类必须是继承一个类,或者实现一个接口;为何?因为要想实例化这个没有名字的类,只能先去实例化它的父类;被创建的对象,带有内容(对象体)–这个内容是为了实现被继承的类或者接口

class Outer{
    int x = 4;
    class Inner{
        void show(){
            System.out.println("x="+x);
        }
    }
    void function(){
        new Inner().show();
    }

    public static void main(String[] agrs){
        new Outer().function();
    }
}

使用匿名内部类实现:
* new 父类或者接口(){定义子类的内容};
* 匿名内部类就是以这个匿名子类对象,而且这个对象有点胖,可以理解为带内容的对象;
* 匿名内部类中的方法不要超过三个;

abstract class FatherClass{//需要有继承
    abstract void show();
}
class Outer{
    int x = 4;
    void method(){
        new FatherClass(){//通过实例化基类对象,获得匿名内部类的内容
            void show(){
                System.out.println("x="+x);
            }
        }.show();
    }
    public static void main(String[] agrs){
        new Outer();
}

(面试)

你可能感兴趣的:(java,面向对象,多态,inheritance)