我发现自己真的没学过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
12import 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
12import 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
20import 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
14import 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
17import 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
31import 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
28import 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 | class Human { |
当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
54class 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
6public 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
9public 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
2abstract 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