类与对象
类有成员+方法
public class MyClass{
private int i;
MyClass(int value){
i = value;
}
public void add(){
this.i++;
}
public void get(){
return i;
}
}
实例化
类可实例化成对象,除了静态方法和成员,只有实例化成对象才能使用。
抽象类
继承
关键字extends
子类会继承父类带有的变量和方法
子类只可继承自一个父类
子类可以在原有父类所带有的变量和方法的基础上,增加其他方法或成员
通过@Override
注解,可对父类的方法进行重写。
public class Animal{
private int age;
public void eat(){
// do something;
}
public void sleep(){
// do something;
}
}
public class Dog extends Animal{
@Override
public void eat(){
System.out.println("eat");
}
public void eat(String food){
System.out.println("eat " + food);
}
@Override
public void sleep(){
System.out.println("sleep");
}
}
接口
- 关键字
interface
定义接口 接口可以定义成员变量和方法
- 成员变量一定是
final
常量,默认用final
与static
修饰,无需显式声明,且定义时,必须赋值public interface FatherI{
// 成员变量,已用final修饰
int a = 1;
// 定义方法,默认已使用public abstract 修饰,实现类必须将方法进行实现
void sleep();
}
- 成员变量一定是
类使用关键字
implements
实现接口- 接口使用关键字
extends
拓展一个或多个的接口 在Java8中,为接口引入
静态
和默认
方法,这时候接口与抽象类的界限变得模糊起来。- 默认方法用
default
关键字修饰,通过实现类的实例化对象才能进行调用 静态方法用
static
关键字修饰,可直接通过接口调用- 在Java9中可用
private
修饰,默认为public
,private
的静态方法只能在接口中访问,即使是在接口的实现类中也不可使用 ```java public interface FatherI{ // 抽象方法,必须被实现类重写(除非实现类是抽象类) void abstractMethod(); // 静态方法 public static void staticMethod(){ System.out.println(“test-FatherI-staticMethod”); } // 私有静态方法 private static void privateStaticMethod(){ System.out.println(“test-FatherI-staticMethod”); } // 默认方法,可被实现类重写 //访问权限为public,我很好奇在后面的版本会不会增加private修饰 default void defaultMethod(){ // 私有静态方法,只可接口内部访问,所以只有接口中实现的方法才可以调用 FatherI.privateStaticMethod(); } } public class FatherImpl implements FatherI{
@Override public void abstractMethod(){ System.out.println(“test-abstractMethod”); }
public static void main(String[] args) { FatherImpl fatherImpl = new FatherImpl();
// 可用,通过实现类的实例化对象调用重写的方法 fatherImpl.abstractMethod();
//!!!不可用,通过实现类调用接口静态方法 FatherImpl.staticMethod();
//!!!可用,调用接口静态方法 FatherI.staticMethod();
//!!!不可用,在其他类中调用私有静态方法 FatherI.privateStaticMethod();
// 可用,通过实现类的实例化对象调用接口默认方法 fatherImpl.defaultMethod();
} } ```- 在Java9中可用
- 默认方法用
如果子类(或实现类)继承的父类和实现的接口中声明了同名同参数的默认方法,那么子类在没有重写此方法的情况下,默认调用的是父类中的同名同参数的方法。 ```java public class FatherC{ private int age; public void test(){
System.out.println("test-FatherC")
} }
public interface FatherI { // 接口默认方法 public default void test(){ System.out.println(“test-FatherI”) } }
public class Son extends FatherC implements FatherI{ private String name; }
public class Main{ public static void main(String[] args){ Son son = new Son(); son.test(); // 输出 test-FaC // 会优先调用父类FaC的test()方法 } }
- 一个类可以实现多个接口,可以弥补抽象类的单继承设计
```java
public interface FatherIA{
void sleep();
default void defaultMethod(){
System.out.println("defaultMethod-FatherIA");
}
}
public interface FatherIB{
void sleep();
default void defaultMethod(){
System.out.println("defaultMethod-FatherIB");
}
}
public class FatherImpl implements FatherIA,FatherIB{
@Override
public void sleep(){
System.out.println("sleep");
}
// 当实现的多个接口,具有相同的默认方法(与方法重载类似),实现类必须自行实现
@Override
public void defaultMethod(){
System.out.println("defaultMethod-FatherImpl");
}
}
封装
隐藏对象的内部状态和功能,仅对外提供公共访问方式。
如下所示,name
和height
属性都设为私有,防止通过person.height
直接访问对象的属性,而允许通过get/set
方法进行访问和修改的方式,就是一个典型的封装行为。
class Person {
private String name;
private double height;
private int age;
public void setName(String name){
this.name = name;
}
public String getName(){
return name;
}
public void setHeight(double height){
if(height < 0){
return;
}
this.height = height;
}
public double getHeight(){
return height;
}
public void setAge(int age){
if(age < 0){
return;
}
this.age = age;
}
public int getAge(){
return age;
}
public boolean isAdult(){
return age >= 18;
}
}
class Test{
public static void main(String args[]){
Person person = new Person();
// 不可用
person.height = 1.70;
// 可用
person.setHeight(1.70);
}
}
好处
- 可以隐藏内部细节,对外提供公共访问方式,提高安全性
- 在
setHeight
时,可以对height
的值进行验证,隐藏了内部细节,提高了安全性。
- 在
提高代码的复用性
- 还是考虑上面的代码,当其他类调用
Person
的isAdult()
方法时,直接调用即可得到结果,当有一天法定成年年龄发生改变,只需改变isAdult()
的实现,而调用者则无需做出任何更改。多态(重载与重写)
多态的定义还挺有争议的,有人把方法重载也算进在,这里也采取这种广义的理解(尽管我觉得不算👀。
Java中分为编译时多态(即方法重载Overload
)和运行时多态(即方法重写Override
)编译时多态(重载)
编译时多态即是方法/运算符的重载,Java中并不支持运算符重载(但我觉得未来可期:>)
同一个事物在不同的情况下具有不同的行为,比如当传入的参数的个数,类型这些不同时,进行不一样处理,这称为多态。
当有多个具有相同名称但参数不同的方法时,则这些方法称为重载。方法可以通过参数数量的更改或/和参数类型的更改来重载。public class MyClass{
private int i;
public void add(){
i++;
}
public void add(int a){
i+=a;
}
}
运行时多态(重写)
简单来说是同一个类的对象,做同一件事,表现出不同的行为。
以下图为例
- 还是考虑上面的代码,当其他类调用
Shape
为父类,Cirle
和Square
为子类,均实现了draw()
方法和move()
方法ShapeController
类具有drawShape()
方法,接受一个shape
类型的参数 ```java public class Shape{ void draw(){
} void move(){System.out.println("draw-default");
} } class Circle extends Shape { @Override public void draw(){System.out.println("move-default");
} @Override public void move(){System.out.println("draw-circle");
} } class Square extends Shape{ @Override public void draw(){System.out.println("move-circle");
} @Override public void move(){System.out.println("draw-square");
} } class ShapeController{ public static void main(String[] args){System.out.println("move-square");
} public void drawShape(Shape shape){Circle circle = new Circle();
Square square = new Square();
drawShape(circle);
drawShape(square);
} }shape.draw();
```java
public class Shape{
void draw(){
System.out.println("draw-default");
}
void move(){
System.out.println("move-default");
}
}
class Circle extends Shape {
@Override
public void draw(){
System.out.println("draw-circle");
}
@Override
public void move(){
System.out.println("move-circle");
}
}
class Square extends Shape{
@Override
public void draw(){
System.out.println("draw-square");
}
@Override
public void move(){
System.out.println("move-square");
}
}
class ShapeController{
public static void main(String[] args){
Circle circle = new Circle();
Square square = new Square();
drawShape(circle);
drawShape(square);
}
public void drawShape(Shape shape){
shape.draw();
}
}
// todo
- 编译器在编译时,并不直接能确定传入
drawShape()
方法的参数具体是什么类型,只能确定传入的一定是Shape
类型。那这时候,通过Shape s = new Circle();
的方式,创建一个Circle对象,调用ShapeController
的drawShape(s)
方法,这时候会调用Circle
类重写的draw
方法,同理,当传入一个Square
类给drawShape(s)
,会调用Square
类的draw
方法。同一个对象,在传入对象不同的自动进行不同行为即为多态。
为了解决这个问题,面向对象程序设计语言使用了
后期绑定
的概念,当向对象发送消息时,被调用的代码直到运行时才能确定。编译器确保被调用方法的存在,并对调用参数和返回值进行类型检查,但是并不知道将被执行的确切代码。 为了执行后期绑定,Java使用一小段特殊的代码来代替绝对地址的调用。这段代码使用对象中存储的信息来计算方法体的地址。这样,根据这一小段代码的内容,每一个对象都可以具有不同的行为表现。当向一个对象发送消息时,该对象就能直到对这条消息该做些什么 ——《Java编程思想》第四版,第一章第9页,陈昊鹏译 评论👀:”向对象发送消息”,不知道是原文如此还是译者自己的译法,这句好实在不好理解 不过总的来说,在这儿提到多态的是实现机制的,还是很受用, 之前觉得多态这种行为是很自然的,理所应该,那实现起来应该也很简单,没想到实现机制还挺复杂