面向对象编程(Object-Oriented Programming )概念
Object-Oriented Programming的基本概念是class和object。
有四个特性: 继承(Inheritance),多态(Polymorphism), 抽象(Abstraction), 封装(Encapsulation)。
这一章详细讲class 和object, 最后讲抽象和封装。
1.object
2. class(类)
3. 创建object的方法
4. static关键词
5. final关键词
6. 嵌套类(nested class)
7. abstract class(抽象类)
8.Interfaces
9. abstract class 和 interface对比
10. 封装
1. object
现实生活中,object一般包括了属性和行为。 java里对应的就是fields和method。
比方说 一条狗包含了属性 (name, color, breed, hungry) 和行为 (barking, fetching, wagging tail)
For each object that you see, ask yourself two questions: “What possible states can this object be in?” and “What possible behavior can this object perform?”.
object 的好处:
- 模块化: 关于object的代码可以独立管理,区别于其他object。object创建之后就可以很容易的被传递。
- 隐藏信息: 把信息藏在object内部,其他object看不到。
- 代码re-use: object被创建之后可以被到处用。
- 很容易被插入和移除。 一旦确定一个object出问题,可以很容易被拿掉。
https://docs.oracle.com/javase/tutorial/java/concepts/object.html
2. class
现实世界中,把同类的object归为一类。比方说
There may be thousands of other bicycles in existence, all of the same make and model.
在oop里,每辆自行车都是一个“自行车类”的实例。
Each bicycle was built from the same set of blueprints and therefore contains the same components.
2.1. class的组成:
- fields: 一些变量,描述属于这个class的object应该有的一些属性。
- [access_modifier] [static] [final] type name [= initial value] ; ([]里为可写可不写的)
- 尽量定义为private的,然后写getter和setter来access这些参数。
- constructor: 构造函数。 用来实例化一个object。
- Constructor(s) 的名字必须和类的名字一样。
- Constructor没有返回值。
- Constructor 不能是abstract, final, static 和 Synchronized.
- 当用
new
来实例化一个object的时候,就会调用构造函数。 - 一个class可以有很多个Constructor,接受不同的参数。
new()
里面有几个参数就会调用相对应参数的构造函数。 - 没有参数的Constructor是默认构造函数。在一个class里,如果没有声明任何的Constructor,系统会分配一个默认构造函数 。 如果声明了任何一个构造函数,系统就不会给了。
this()
方法用来调用自己的构造函数。
- method: 成员方法。
- [access_modifier] [final] return_type name(parameter_list)[Exception list ]; ([]里为可写可不写的)
- Method signature: 包含方法名字和参数。
- 名字要尽量为动词。描述方法作用。
- local variables: 局部变量,是定义在方法里面的变量。
- method的执行是在stack上的,详情在JAVA 面试知识点 2 --空间分配
- 构造函数(Constructor) vs. 成员方法(method)
- 构造函数的名字必须和类名一模一样。成员方法名可以和类名一样,但是极度不推荐。
- 构造函数没有返回值,成员方法必须有返回函数,如果meiyou返回值,则必须声明为
void
- 从object层面来看,构造函数只在实例化object的时候被调用一次。但是可以调用method很多次。
一个基本的class和object 例子(包含上面所有要点):
class Animal { //定义一个Animal的class,就是这么简单
//fields. 每个animal应该有的属性,尽量设置为private。
private boolean vegetarian;
private int noOfLegs;
//default constructor. 默认构造函数(没有参数的构造函数)。
//如果一个class没有写构造函数,那么系统就会给一个默认的。
public Animal(){
// Animal a = new Animal()会调用这个constructor。
System.out.println("an animal created");
}
//每个class可以有很多个不同参数的构造函数,用new创建一个object的时候会根据不同的参数选择构造函数。
//如果class写了构造函数,不带参数的构造函数就不会被系统添加,如果要用到,需要手动添加。
// Animal a = new Animal(true, 4)会调用这个constructor。
public Animal(boolean vegetarian, int legs){
//同一个class里的constructor可以被互相用this调用。
this(); //this()用来调用自己的其他的构造函数(省代码)
this.vegetarian = vegetarian;
this.noOfLegs = legs;
System.out.println("an animal with " + legs + " legs created");
}
//methods 和构造函数不同的是,method的声明必须带返回类型
public boolean isVegetarian() {
return vegetarian;
}
//如果没有返回类型,就写个void
public void setVegetarian(boolean vegetarian) {
this.vegetarian = vegetarian;
}
public int getNoOfLegs() {
return noOfLegs;
}
public void setNoOfLegs(int noOfLegs) {
this.noOfLegs = noOfLegs;
}
//上面这几个都是getter和setter
public void eat() {
System.out.println("animal can eat");
}
public void eat(String food) {
System.out.println("animal can eat" + food);
}
//上面两个方法同名不同参数, 叫做overload(请与override区分开来)。
}
public class AnimalTest{
public static void main(String []args){//main 方法是程序的入口。
Animal a = new Animal(true, 4); //创造了一个Animal的object,叫做a.
a.eat();
a.eat("onion");
//通过getter和setter来设置private变量。
System.out.println(a.getNoOfLegs());
a.setNoOfLegs(8);
System.out.println(a.getNoOfLegs());
}
}
运行结果:
an animal created
an animal with 4 legs created
animal can eat
animal can eatonion
4
8
3. 创建object的方法
object和class是两个层面。
- class通常只定义出大概的框架及模版。以及所有属于这个类的object通用的属性。
- object是class的具体的,真实存在的实例,每一个object都要有:
- 状态 : 一个object的所有属性和状态(fields)。
- 行为 : 一个object的methods。
- Identity : 每一个object都要有一个reference,用来标识这个object。
- 注意: 非static的状态和行为都是属于object的,只能通过object来access。而static的状态和行为是属于class的。在下面详细说。
3.1 class的实例化(就是创建一个这个class的object)
- 用
new
上面说了。就是用constructor。 - 用
Class.forName(String className)
属于Reflection。一般用不到。Test obj = (Test)Class.forName("com.p1.Test").newInstance();
- 用
clone()
方法// 创建一个 Test的实例 Test t1 = new Test(); // 用clone创建另一个实例 Test t2 = (Test)t1.clone();
- 用
deserialization
序列化和反序列化,通常是用来在远距离协作的时候可以打包传递object。FileInputStream file = new FileInputStream(filename); ObjectInputStream in = new ObjectInputStream(file); Object obj = in.readObject();
3.2 匿名对象(Anonymous objects)
Anonymous objects 是实例化之后但是并没有把它赋值给一个reference。
- 就是在方法调用的时候现用现建
- 方法结束之后就被销毁
- 下面例子里
new AnonymousTest()
创建了一个object但是并没有赋值给一个reference.public class AnonymousTest { public String message(){ return "Hello World!"; } public static void main(String[] args) { System.out.println(new AnonymousTest().message()); } }
4. static关键词
static 关键词可以用来修饰
代码块(blocks)
变量(variables)
方法(methods)
内部类(inner classes)
被static修饰过的member(block,variable,method,nested class)称为static member。 static member可以被类名直接access。不用创建任何的object。
static定义过的member是class级别的
4.1. Static variables
- 一个static的变量是class级别的。只会创建一个。所有的这个类的object会共享这个变量。如果一个object改变了这个变量的值,其他object也会被影响。算是全局变量。
- static的变量只能在class的级别定义,不能在方法里定义(local variable)。static的方法里也不行。
4.2. Static methods
- 程序的入口,
main( )
是static的。 - 一个static的方法可以用class的名字直接调用。不需要创建任何object。
- 在同一个class里面,static的方法只能被static的方法直接调用,
- static的方法只能直接access static的variable
- 不能跟this或者super扯上关系
class StaticTest {
// static variable
static int a = 10;
// non-static variable
int b = 20;
// static method
static void m1(){
a = 20; // 可以的
b = 10; // 不可以的!compilation error
m2(); // 不可以的!compilation error
System.out.println(this.b); // 不可以的!compiler error
}
// non-static method
void m2() {}
public static void main(String[] args){
m1(); //main 方法也是static的!
StaticTest st = new StaticTest();
st.m2(); //使用m2的正确方法
System.out.println(st.b) //调用b的正确 fangfa
StaticTest2.m3();//直接用class名字调用
}
}
class StaticTest2 {
static void m3(){
System.out.println("StaticTest2 --> m3!")
}
}
4.3. 一个class可以是static的么?
答案: 只有inner classes 可以是static的。(详情在# 6. nested class)
4.4. static block:
- static block在class被load的时候被执行,且只执行一次。通常用来初始化一些值。
- static block 比main方法还要早,因为是在class一开始load的时候就执行了
- 只能给static的variable赋值
- static 的variable和block根据他们在程序里的顺序执行。
输出:public class StaticTest3 { // static variable static int a = 10; int b; // static block static { System.out.println("Static block!"); System.out.println("a : "+a); //b = a * 4; // 不可以的!只能给static的variable赋值 } public static void main(String[] args) { System.out.println("Main!"); } }
Static block!. a : 10 Main!
5. final关键词
final关键词可以用来修饰
变量(variables)
方法(methods)
类(class)
final 的变量 --> 这个变量不可以被重新赋值
final 的方法 --> 这个方法不可以被override
final 的类 --> 这个类不可以被继承
一个个来看:
1. final的变量:
- 不能被重新赋值。 不能被重新赋值的意思是reference不能再变了,不代表value就不能变了。例如:
final int FINALA = 5; // FINALA = 6 这是不行的 final String[] FINALB = new String[2]; FINALB[0] = hello; // 这个可以的,这不是重新赋值给'FINALB'。 而是给它的一个元素的。 // FINALB = new String[3]; 这是不行的
- final变量的赋值。因为不能被重新赋值,所以final变量的赋值只有三种:
- 在定义的时候就被赋值,这个是最常见的(例子在?)。
- 在instance-initializer block或者是constructor里。(例子在?)
- 如果是一个static final的变量,可以在static block里面赋值。(例子在?)
class FinalTest { final int FINALC; final int FINALD; static final int FINALE; // instance initializer block { FINALC = 25; } FinalTest(){ FINALD = 35; } // static block static { FINALE = 45; } }
- 在方法或者是构造函数里面创建的final的variable叫local final variable。必须被立马赋值。
- final 在foreach loop里是合法的!!!!因为
i
的scope
是forloop里面。其实每次i
都是被重新定义了一次。
class FinalTest2 {
public static void main(String[] args){
int arr[] = {1, 2, 3};
for (final int i : arr) //合法的!!!!!
System.out.print(i + " "); //输出为: 1 2 3
}
}
2. final的方法:
- final的方法是不可以被override的。
- 通常是对系统的保护。比如object里面很多方法都是final的。
3. final的类:
- final的类不能被继承。
- 通常用来做immutable class。
6. 嵌套类(nested class)
java里面class可以嵌套,叫做nested class
。外面的class叫做outter class
,里面的class叫做inner class
.
大致看上去:
class Outer_Class {
class Inner_Class {
}
}
根据inner class的不同,可以分为两类,static inner class
和non-static inner class
其中non-static inner class 比较复杂。
6.1 non-static inner class
为什么 要有inner class。因为,安全啊!。因为class是不能加private的,但是,inner class可以!
non-static inner class又根据所在位置分为三种
- Inner Class
- Method-local Inner Class
- Anonymous Inner Class
6.1.1 Inner Class
- outter class 的private 的变量可以被inner class 读取。
- private的inner class不能被外界access,如果需要,outter class就需要向外界提供接口
- public 的inner class可以从外面access,但是需要先创建outter class的一个对象才能通过这个object来access inner class
- 创建inner class的object略复杂。需要先创建outter class的object。例子:
输出:class Outer_Demo { int num1 = 4; private int num2 = 8; private class Inner_Demo1 { // inner class可以设置为private的 public void print() { System.out.println("num:" + num); } } public class Inner_Demo2 { public int getNum() { return num2;//inner class可以直接读取outter class的private变量。 } } void display_Inner() { //Inner_Demo不能从outter外面access Inner_Demo1 inner = new Inner_Demo1(); inner.print(); } } public class NestedTest { public static void main(String args[]) { Outer_Demo outer = new Outer_Demo(); outer.display_Inner(); //这一行有点复杂 //1. inner2的名字是outter.inner //new inner()跟在outter后面 Outer_Demo.Inner_Demo2 inner2 = outer.new Inner_Demo2(); System.out.println("num2:" + inner2.getNum()); } }
num1:4 num2:8
6.1.2Method-local Inner Class
- 跟local variable一样,local inner class就是定义在方法里面的class:
- 这种class只能在方法执行的时候被实例化。
public class Outer_Demo2 { void my_Method() { // non-static method int num = 23; // method-local inner class class MethodInner_Demo { public void print() { System.out.println("num"+num); } } // end of inner class MethodInner_Demo inner = new MethodInner_Demo(); inner.print(); } public static void main(String args[]) { Outerclass outer = new Outerclass(); outer.my_Method(); } }
6.1.3 Anonymous Inner Class(匿名内部类)
- 这个用的比较多,匿名类就是定义类的时候并没有定义类名(请和匿名对象区分开)。一边实例化对象一边定义类。通常用来override 一个abstract class或者interface方法的时候。
解释: 上面的例子里abstract class AnonymousTest{ public abstract void m1(); } public class NestedTest2 { public static void main(String args[]) { //定义了一个AnonymousTest的子类,并创建了一个对象,赋值给了AnonymousTestn 类型的对象。 AnonymousTest inner = new AnonymousTest() { //AnonymousTest的子类,也是NestedTest2的内部类的body public void m1() { System.out.println("m1 is defined here"); } }; inner.m1(); } }
AnonymousTest
是一个abstract class,提供了一个m1的abstract 方法,在NestedTest2(outter class)里,实际上是定义了一个AnonymousTest
的子类(inner class),并且用这个子类创建了一个对象inner
- 匿名内部类也可以当作参数,比较常用的是collection里的排序规则:
//Comparator是一个interface,里面有一个compare方法 TreeSet<String> treeSetObj = new TreeSet<String>(new Comparator<String>() { public int compare(String i1,String i2) { return i2.compareTo(i1); } });
6.2 static inner class
- 一个static的inner class是一个outter class的static member。
- 并不需要创建outter class的对象来access。
- 仅可以access outter class 的static member。
public class Outer { static int num = 4; static class Static_Inner { public void m1() { //只可以读取外部类static的变量 System.out.println("num:" + num); } } public static void main(String args[]) { //直接用实例化的时候并不需要outter class的对象。 //但需要`外部类名.内部类名`来access。 Outer.Static_Inner nested = new Outer.Static_Inner(); nested.m1(); } }
7. abstract class(抽象类)
如果在父类里面,有一个方法没有implementation,只有定义,那么这个方法就是abstract的。如果一个class里面有一个或者多个abstract方法,那么这个class就是abstract的。abstract的方法和class需要在定义中加一个abstract
关键词。
abstract class 和普通的class是一样的,除了不能被实例化之外
- abstract的方法没有body, abstract的class和普通class一样,构造函数不能是abstract的。一个abstract class的例子:
abstract class Shape { int color; Shape(){} //构造函数不能是abstract的! // 一个abstract的方法,后面没有大括号。 abstract void draw(); }
- 在java里面,一个class就算没有abstract的方法,也可以定义为abstract的。这个类只能用来继承,不能用来被实例化。
- 一个abstract的类也可以有final的方法(不能被override的)
abstract class Base { final void fun() { System.out.println("Derived!"); } } class Derived extends Base {} class Main { public static void main(String args[]) { Base b = new Derived(); //Base可以作为reference但是不能跟在new后面 b.fun(); } }
- 一个final class不能有 abstract 方法。
- 一个abstract class不能是final的。
8.Interfaces
先说传统的interface。这玩意儿不是class,但是和class很像,有自己的变量和方法。只是所有的变量都默认是public的,static且 final的,所有的方法都是没有body的。
- 一个更高层次的抽象。
- java不能继承多个父类,但是可以
implements
多个interface。 - 也是为了让整个架构调理更清晰。
- interface定义了一个class必须做什么,但是不会告诉你怎么做,是一个class的蓝图和框架。
- 如果一个class implements了一个interface但是并没有提供其中某个方法的实现。则这个方法必须被声明为abstract的,然后这个class也必须是abstract的。
- 用
implements
来implement一个interfaceinterface Vehicle { void changeGear(int a); void speedUp(int a); void applyBrakes(int a); } class Car implements Vehicle { //必须实现上面三个方法 }
8.1 interfaces:
- 我们不能创建interface的实例,但是可以用interface的reference去指向那些implement了它的class创建的实例。
- 一个class可以implement多个interface。
- 一个interface可以
extends
另外的一个或者多个interfaceinterface inter extends inter1, inter2, inter3 { // methods }
- 如果一个class impleents了一个iterface,那它必须实现这个interface里所有的方法。
- interface里所有的方法都是public 且 abstract. 所有的变量都是 public, static, 且 final.
8.2 interfaces 在java8里的新属性:
- 可以有默认的方法定义。
interface Vehicle { default void speedUp(int a) { System.out.println("speedup:" + a); } } class Car implements Vehicle { public static void main (String[] args) { Car c = new Car(); c.speedUp(5); } }
- 可以有static的方法了。 static的方法不能被继承,可以通过类名直接被调用。
8.3 interfaces 在java9里的新属性:
- Static methods
- Private methods
- Private Static methods
9. abstract class 和 interface对比
level1面试问题: abstract class和interface有什么不同?
level2面试问题: 什么时候选择abstract class,什么时候选择interface?
9.1: abstract class和interface有什么不同?
- 方法: interface只有abstract的方法, abstract class可以有abstract和非abstract的方法。从java8开始,interface可以有默认的和staic的方法。
- 变量: interface里的变量默认是public static final的。abstract class可以有各种变量。
- 继承方法: interface用
implements
。 abstract class 用extends
。一个class可以implements很多个interface,只能extends一个abstract class。 - 互相继承:一个interface只能
extends
其他interface。但是abstract class可以继承其他的普通class, abstract class,和interface。
9.2什么时候选择abstract class,什么时候选择interface?
。。。凭感觉吧。。。
-
在以下的情况下考虑abstract class:
- 在整个application里面,有一些class里面的一些方法是可以共享的,就可以把这些代码提炼出来放到astract class里的一个non-abstract的方法里。
- 需要non-static 或者 non-final 的变量的时候。
- 如果有一些class有一堆相同的fields或者方法,那么可以考虑把这些共同的东西放到一个abstract class里,然后这些class去
extends
这个abstract class就可以。
-
在以下的情况下考虑interface:
- 一些class需要实现某些共同的方法,但是如何实现没有任何共同点。
- 定义一系列的行为,但是并不关心谁来实现以及如何实现这些行为。
- 如果没有特定需要abstract class(不满足上面那些条件), 则用interface,因为interface可以多重继承。
一个比较常用的interface:
Interface Comparator
在java collection里面有很多interface和abstract class的运用。
10.封装(Encapsulation)
封装没啥好说的,就是把所有的东西包到一个unit里面去(object)。 这样自己的值只能自己去access。