1.封装
1.1访问权限修饰符
public: 修饰的类、方法、变量,在当前包和其他包都有访问权限;protected: 应用于继承的封装限制,允许子类和同一个包的类访问,禁止跨包非子类的访问;default: 不加任何访问修饰符,允许当前包的类访问,禁止跨包访问,无论是不是子类;private: 限制最严格,仅允许当前类访问,当前包的其他类和跨包都禁止访问;
Java 定义了 package 包,用来区分类名的命名空间。
包采用了树形目录的存储方式,每个包是一个命名空间,不允许命名重复,不同包中类名可以重复。
例如package org.apache.commons.lang;,可以理解为一个目录,下面有很多 .java 文件,每个 java 文件都有一个同名的类
- org
- apache
- commons
- lang
ArrayUtils.java // 在其中的 public class ArrayUtils 中是当前类
BooleanUtils.java // public class BooleanUtils 相对于 ArrayUtils 是同一个包的类
MyArrayUtils.java // 自定义继承 ArrayUtils,是 ArrayUtils 的子类
- collections
ArrayStack.java // public class ArrayStack 是相对于 ArrayUtils 不同包的非子类
Buffer.class
MyArrayUtils.java // 自定义继承 ArrayUtils,是 ArrayUtils 的子类
| 访问权限修饰符 | 同一个类 | 同一个包的类 | 子类 | 不同包的非子类 | 修饰对象 |
|---|---|---|---|---|---|
| public | √ | √ | √ | √ | 类,方法,变量 |
| protected | √ | √ | √ | × | 方法,变量 |
| 不写(缺省) | √ | √ | × | × | 类,方法,变量 |
| private | √ | × | × | × | 方法,变量 |
public class SomeClass {
public int i; // 可以被所有类访问
protected boolean b; // 可以被所有子类和本包的类访问
void print() {} // 可以被本包的类访问
private class InnerClass {} // 只能被本类访问
}
抽象方法不能用
private访问权限修饰符,因为抽象方法需要继承的子类实现,不能定义为私有的。
变量一般不定义为
public,这样失去了封装的控制,一般定义为private,通过public的getter和setter对外暴露。如果是内部类的变量,可以直接暴露修改。
main()方法必须定义为public,否则,Java 解释器无法运行该类。
1.2其他修饰符
-
static: 修饰方法、变量和代码块
表达被修饰的方法和变量是否需要实例化使用,有static修饰时可以不实例化,直接使用类名.方法/变量来表达,只有一份。
静态变量存储于类中,无论实例化多少对象,都只有一份静态变量;静态方法只能使用类的静态变量,禁止使用
this/super关键字,因为不在实例化的对象中;
静态代码块在 JVM 加载类时会执行,和静态方法的区别是项目启动即执行,而静态方法是启动时初始化,在被调用的时候被动执行。 -
final: 修饰类,方法和变量
修饰的类不可继承,修饰的方法不可被子类重新定义(重写override),但是可以被子类使用和重载,修饰的变量不可修改。
经常与static连用,设置全局常量,final修饰变量不可修改,static修饰变量在实例化前引用。
与abstract修饰符反相关,一个对象如果是final的就不是abstract的,反之。
// 修饰变量
public class Person {
public static final String NAME = 'tom';
public final String getName() { return "hello";}
}
// 修饰方法
public class Employee extends Person {
@Override
public final String getName() {} // compilation error: overriden method is final
public final String getName() {
String s = super.getName(); // 子类可以使用父类的方法,并重载
return s + 'sub';
}
}
// 修饰类
final class User {}
class Teacher extends User {} // compilation error: cannot inherit from final class
-
abstract: 修饰类和方法
抽象类不可实例化,如果一个类包含抽象方法,则一定是抽象类,抽象类可以没有抽象方法。抽象方法的实现由子类完成,抽象方法不可被static,final修饰。 -
synchronized: 并发控制,修饰代码块和方法
代码块可以指定锁住的对象,方法被锁住就是该方法。 -
volatile: 并发控制,修饰变量
被修饰变量保证不同线程对这个变量操作的实时可见性,同时禁止指令重排。 -
transient: 修饰变量,不能修饰本地变量
被修饰的变量所属类需要实现Serializable接口,用来标记不需要序列化的变量。
注意,当transient和final连用时,被修饰变量依然会被序列化,因为final将变量引用为常量表达式,此时忽略了transient关键字。 -
native: 修饰方法
当 java 和其他语言协作时,使用native修饰,被修饰方法是原生函数实现,如果是C/C++实现的则编译成了DLL被 java 调用。
2.继承
2.1抽象类
顾名思义,用于定义结构,抽象类不可以实例化,一般会包含抽象方法,但也可以写一般方法。
一般使用场景是被子类继承,实现所有抽象方法后,子类可以被实例化。
public abstract class AbstractClass {
protected int x;
public abstract int getX();
public void someFunc(){}
}
public class AbstractExtend extends AbstractClass {
@Override
public int getX(){}
}
2.2接口
是抽象类的延伸,所有变量和方法必须是 public,变量都是 static 和 final 的。
因为只能继承一个类,所以抽象类使用上有局限,而一个类可以实现多个接口;但接口对字段的封装特性做了约束,有局限,抽象类可以定义多种访问权限。
一般需要使用多重继承的使用接口,需要在多个类中共享代码、控制访问权限使用抽象类。
接口具有特殊性,成员的访问权限编译器会自动补全,可以少定义,但不能定义错。
变量:public static final
抽象方法: public abstract
静态方法:public static
内部类和内部接口:public static
3.多态
3.1重写 Override
子类实现了父类声明上完全相同的一个方法,要求:
- 子类方法的访问权限必须大于等于父类方法
- 子类方法的返回类型必须是父类方法返回类型或者其子类型
这两个要求是为了实现里式替换原则 LSP,即在任何条件下可以使用父类的地方,可以用子类对象替换,保证原有承昫的逻辑行为和正确性。
3.2重载 Overload
同一个类中,方法重名时,如果参数类型、个数、顺序至少一个不同是重载。
注意,返回值类型不同,不是重载。
4.Object 通用方法
有一些功能是通用性的,Java 定义在了 Object 中。
public final native Class<?> getClass();
public native int hashCode();
public boolean equals(Object obj) {
return (this == obj);
}
protected native Object clone() throws CloneNotSupportedException;
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
public final native void notify();
public final native void notifyAll();
public final native void wait(long timeout) throws InterruptedException;
protected void finalize() throws Throwable { }
5.总结
Java 在语言层面较好的支持了面向对象的编程范式。在封装中要注意访问权限,继承关系,可变性;继承中灵活使用抽象类和接口,以支持访问权限定义和多重继承;在多态中通过重写和重载复用代码,提高开发效率。