这些java面向对象的知识,你都知道吗?


  我发现自己真的没学过java…… o(╯□╰)o

  这篇总结可以帮助新手复习,或者查缺补漏(虽然还不太全面),也欢迎老手给指正错误。

面向对象特征

  • 封装(encapsulation)
  • 继承(inheritance)
  • 多态(polymorphism)

成员变量与局部变量

  • 成员变量 局部变量
  • 类中位置不同:类中方法外 方法内或者方法声明上
  • 内存位置不同:堆内存 栈内存
  • 生命周期不同:随对象存在或消失 随方法调用存在或消失
  • 初始值不同: 有默认的初始化值 必须先定义,赋值,才能使用

封装

  • 隐藏对象的属性和实现细节,仅提供对外公共访问方式
  • 将变化隔离、便于使用、提供代码复用性、提高安全性

四种权限修饰符

 访问权限   本类   本包   子类   其他包 
public
protect
default
private

如果不写为默认,即default。

构造方法

  • 函数名与类名相同
  • 不用定义返回值类型,不用写return
  • 作用:给对象初始化
  • 多个构造方法以重载形式存在
  • 不写会默认一个空的构造方法

Student s = new Student();在内存做了哪些事情?

  • 加载Student.class文件进内存
  • 在栈内存为s开辟空间
  • 在堆内存为s对象开辟空间
  • 对s的成员变量进行默认初始化
  • 对s的成员变量进行显示初始化
  • 通过构造方法对学生对象的成员变量赋值
  • 学生对象初始化完毕,把对象地址赋值给s变量

第四点:局部变量定义之后必须赋值才能用,而类中的成员变量不赋值便可以用,这时候 执行的是默认初始化。如下程序输出 0 ;

1
2
3
4
5
6
7
8
9
10
11
12
import java.util.*;

class Student{
int age;
}

class Test1{
public static void main(String [] args){
Student s=new Student();
System.out.print(s.age);
}
}

第五点:显式初始化,如下程序输出5;

1
2
3
4
5
6
7
8
9
10
11
12
import java.util.*;

class Student{
int age=5;
}

class Test1{
public static void main(String [] args){
Student s=new Student();
System.out.print(s.age);
}
}

static关键字

  可以用来修饰成员变量和成员方法

修饰成员变量

  • 随着类的加载而加载
  • 优先于对象而存在
  • 被类的所有对象共享(最明显的特点),如下程序:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    import java.util.*;

    class Student{
    static int a=333;
    int b=333;
    }

    class Test1{
    public static void main(String [] args){
    Student s1=new Student();
    Student s2=new Student();
    System.out.println(s2.a+" "+s2.b);
    s1.a=666;
    s1.b=666;
    System.out.println(s2.a+" "+s2.b);
    }
    }
    //输出结果:
    //333 333
    //666 333
  • 可以通过类名调用

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    import java.util.*;

    class Student{
    static int a=333;
    int b=333;
    }

    class Test1{
    public static void main(String [] args){
    System.out.println(Student.a);
    //System.out.println(Student.b);
    //类名直接调用a可以,调用b不可以。
    }
    }

修饰成员函数

  • 不能使用this或super关键字
  • 只能访问静态的成员变量和成员方法

静态变量与成员变量区别

  • 静态变量       成员变量
  • 属于类       属于对象
  • 存在于静态区       存在于堆内存
  • 随着类加载而加载       随着对象创建而存在
  • 可以类名调用,可以对象调用       只能对象调用

Java修饰符关键字的顺序

  顺序交换不会报错,但是这些约定俗成的东西还是有必要的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import java.util.*;
class Test1{
public static final int a=1;
static final public int b=1;
final public static int c=1;
static public final int d=1;
final static public int e=1;

public static void main(String [] args){
System.out.print(a);
System.out.print(b);
System.out.print(c);
System.out.print(d);
System.out.print(e);
}
}
// 输出结果:11111

保留字

  保留字是为java预留的关键字,他们虽然现在没有作为关键字,但在以后的升级版本中有可能作为关键字。

  • goto 跳转
  • const 静态,是一个类型修饰,与final有些类似
  • native 本地

transient

  待定。。。

继承

  多个类中存在相同属性和行为时,将这些内容抽取到单独的一个类中,其他类只要继承到那个类即可。

用法:

1
class student extends person{}

好处:

  • 提高了代码复用性
  • 提高了代码可维护性
  • 类之间产生了关系,是多态的前提,但也是一个弊端,增强了类的耦合性

notice:

  • 只能单继承,不支持多继承
  • 但是可以多层继承
  • 子类只能继承父类所有非私有的成员,私有成员不会被继承
  • 不能继承父类的构造方法,但是可以通过super去访问父类构造方法

super关键字

  • this代表本类对应的引用
  • super代表父类存储空间的标识

继承中构造方法的关系

  子类中所有构造方法默认都会访问父类空参数构造方法:
  因为子类会继承父类中的数据,所以子类初始化之前,要先完成父类数据初始化。   

方法重写

  • 子类中出现和父类中一样的方法声明,称为方法覆盖或方法重写。
  • 父类中私有方法不能被重写
  • 重写父类方法,访问权限不能更低
  • 父类静态方法,子类也得通过静态方法重写

final关键字

  • “最终”的意思
  • 修饰类,类不能被继承
  • 修饰变量,变量就成了常量,只能被赋值一次
  • 修饰方法,方法不能被重写

多态

  同一事物,在不同时刻表现出来不同状态

1
2
3
猫 m = new 猫();
动物 d = new 猫();
//猫可以是一只猫,也可以是动物

多态前提和体现

  • 有继承关系
  • 有方法重写
  • 有父类引用指向子类对象

好处

  • 提高了程序的维护性(修改代码的话只修改父类即可,利于维护)
  • 提高了程序的扩展性(若想扩展功能,在子类扩展即可)

弊端

  • 不能访问子类特有功能

向上转型

从子到父,父类引用指向子类对象。我的理解就是本来new了一个Student对象,然后把他(向上)转为父类的Person类型。
用法:

1
Person p=new Student();

举个栗子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import java.util.*;

class Person{
int per;
void show1(){
System.out.println("this is class of person.");
}
}

class Student extends Person{
int stu;
void show1(){
System.out.println("this is class of Student1.");
}
void show2(){
System.out.println("this is class of Student2.");
}
}

class Test1{
public static void main(String [] args){
Person p=new Student();
p.show1();
//p.show2();//p无法访问子类Student的方法
System.out.println(p.per);
//System.out.println(p.stu);//p无法访问子类Student的成员变量
}
}
// 输出结果:
// this is class of Student1.
// 0

  • 向上转型会失去子类的中的某些成员(成员变量和成员方法),”某些”是指父类中没有的那些成员。即p只能访问父类中有的成员,并且由于子类Student类已经重写show1,所以p实际调用的是重写之后的show1。

向下转型

从父到子,父类引用转为子类对象。即把Person类型的p(向下)转为Student类型。
用法:

1
Student s=(Student)p;

举个栗子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import java.util.*;

class Person{
void show1(){
System.out.println("this is class of person.");
}
}

class Student extends Person{
void show2(){
System.out.println("this is class of Student2.");
}
}

class Test1{
public static void main(String [] args){
Person p=new Student();//方法一正确
//Person p=new Person();//方法二会有强制类型转换异常
//Exception in thread "main" java.lang.ClassCastException:
//Person cannot be cast to Student at Test1.main(Test1.java:24)
Student s=(Student)p;
s.show1();
s.show2();
}
}
// 输出结果:
// this is class of person.
// this is class of Student2.

  • 经过尝试,向下转型之前必须先向上转型,采用方法一可以。采用方法二会报错。

多态练习①

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
class Human {  
public void sleep() {
System.out.println("Human sleep..");
}
}
class Male extends Human {
public void sleep() {
System.out.println("Male sleep..");
}
}
class Female extends Human {
public void sleep() {
System.out.println("Female sleep..");
}
}
class MaleStudent extends Male {
public void sleep() {
System.out.println("MaleStudent sleep..");
}
}
class Test1{
public static void main(String[] args) {
dosleep(new Male());
dosleep(new Female());
dosleep(new MaleStudent());
}

public static void dosleep(Human h) {
h.sleep();
}
}
// 输出结果:
// Male sleep..
// Female sleep..
// MaleStudent sleep.

当new Male()传入dosleep()时,执行的是Human h=new Male();即向上转型。无论传入的是Male,Female,还是MaleStudent,都只用一个dosleep接收就可以,也不用重载dosleep函数,提高了代码的复用性。

  • 当new Male()传入dosleep()时,执行的是Human h=new Male();即向上转型。无论传入的是Male,Female,还是MaleStudent,都只用一个dosleep接收就可以,也不用重载dosleep函数,提高了代码的复用性。

多态练习②

这个栗子有点恶心,但是对理解多态会有帮助的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
class A {    
public String show(D obj) {
return ("A and D");
}
public String show(A obj) {
return ("A and A");
}
}

class B extends A{
public String show(B obj){
return ("B and B");
}
public String show(A obj){
return ("B and A");
}
}

class C extends B{}

class D extends B{}

class Test1{
public static void main(String[] args) {
A a1 = new A();
A a2 = new B();
B b = new B();
C c = new C();
D d = new D();

System.out.println("1--" + a1.show(b));
System.out.println("2--" + a1.show(c));
System.out.println("3--" + a1.show(d));

System.out.println("4--" + a2.show(b));
System.out.println("5--" + a2.show(c));
System.out.println("6--" + a2.show(d));

System.out.println("7--" + b.show(b));
System.out.println("8--" + b.show(c));
System.out.println("9--" + b.show(d));
}
}
/* 输出结果:
1--A and A
2--A and A
3--A and D
4--B and A
5--B and A
6--A and D
7--B and B
8--B and B
9--A and D
*/

对于前三个,a1有接收A类型和D类型的两个方法,所以第三个a1.show(d)就会调用show(D obj)来接收,而对于1,2的b和c,c继承b,b继承a,所以b,c都向上转型,调用的是show(A obj)。
对于4,5,6,A a2 = new B(); B向上转型成了A类型,就损失了show(B obj),而B中将show(A obj)重写了,所以a2中有以下两个方法:

1
2
3
4
5
6
public String show(D obj) {    
return ("A and D");
}
public String show(A obj){
return ("B and A");
}

当a2.show()传入的是b或c时,b,c都向上转型成A类型的,调用的是a2中的show(A obj)方法,当传入d时,调用a2中的show(D obj)方法。
对于7,8,9,B b = new B(); 由于B继承A,所以B中有以下三个方法:

1
2
3
4
5
6
7
8
9
public String show(D obj) {    
return ("A and D");
}
public String show(A obj){
return ("B and A");
}
public String show(B obj){
return ("B and B");
}

当b.show()传入b,c时,b和c都向上转型,然后调用的是show(B obj),传入d时,直接调用show(D obj)。

抽象类

  • 在java中一个没有方法体的方法应该定义为抽象方法,在类中如果有抽象方法,该类必须定义为抽象类。

    1
    2
    没有方法体:public abstract void noMethodBody();
    空方法体: public void empertyBody(){}
  • 格式

    1
    2
    abstract class className{}
    public abstract void method();
  • 抽象类不能实例化,可以由具体的子类实例化

接口

  • 比如猫狗是继承动物类,但是有些猫狗被训练有了钻火圈,跳高的功能,这些能力本来不属于猫狗,是经过训练得到的,或是说经过扩展得到的,java就是通过接口来定义这些额外扩展的功能。
  • 定义格式:interface interfaceName{}
  • 实现格式:class className implements interfaceName{}
  • 接口不能实例化,由具体的子类实例化。
  • 成员变量默认修饰符: public static final
  • 没有构造方法,接口主要用来扩展功能,没有具体存在
  • 只能有抽象方法,默认修饰符 public abstract

类与类,类与接口,接口与接口 的关系

  • 类与类:继承关系,只能单继承,可以多层继承
  • 类与接口:实现关系,可以单实现,可以多实现,可以继承一个类的同时实现多个接口
  • 接口与接口:可以单继承,可以多继承

抽象类与接口的区别

成员区别

  • 抽象类:变量、常量、抽象方法、非抽象方法
  • 接口:常量、抽象方法

设计理念区别

  • 抽象类:共性功能
  • 接口: 扩展功能

如有错误,还请指正。

欢迎与我分享你的看法。
转载请注明出处:http://taowusheng.cn/
微博:寒枫–0-0–
知乎:https://www.zhihu.com/people/tao-wu-sheng
豆瓣:YIFEI