Java笔记1-面向对象基础


笔记摘自 廖雪峰的官方网站-Java教程.
网站提供在线运行Java代码功能.

面向对象基础

构造方法

示例

public class Main {
    public static void main(String[] args) {
        Person p1 = new Person("Xiao Ming", 15); // 既可以调用带参数的构造方法
        Person p2 = new Person(); // 也可以调用无参数构造方法
    }
}

class Person {
    private String name;
    private int age;

    public Person() {
    }

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    
    public String getName() {
        return this.name;
    }

    public int getAge() {
        return this.age;
    }
}

方法重载

方法名相同,但各自的参数不同,称为方法重载(Overload)。
方法重载的返回值类型通常都是相同的。
示例

class Hello {
    public void hello() {
        System.out.println("Hello, world!");
    }

    public void hello(String name) {
        System.out.println("Hello, " + name + "!");
    }

    public void hello(String name, int age) {
        if (age < 18) {
            System.out.println("Hi, " + name + "!");
        } else {
            System.out.println("Hello, " + name + "!");
        }
    }
}

示例
String类提供了多个重载方法indexOf(),可以查找子串:

  • int indexOf(int ch):根据字符的Unicode码查找;
  • int indexOf(String str):根据字符串查找;
  • int indexOf(int ch, int fromIndex):根据字符查找,但指定起始位置;
  • int indexOf(String str, int fromIndex)根据字符串查找,但指定起始位置。

继承

Java使用extends关键字来实现继承.
示例

class Person {
    private String name;
    private int age;

    public String getName() {...}
    public void setName(String name) {...}
    public int getAge() {...}
    public void setAge(int age) {...}
}

class Student extends Person {
    // 不要重复name和age字段/方法,
    // 只需要定义新增score字段/方法:
    private int score;

    public int getScore() {}
    public void setScore(int score) {}
}

Java只允许一个class继承自一个类,因此,一个类有且仅有一个父类。只有Object特殊,它没有父类。
在Java中,任何class的构造方法,第一行语句必须是调用父类的构造方法。如果没有明确地调用父类的构造方法,编译器会帮我们自动加一句super();
super关键字表示父类(超类)。子类引用父类的字段时,可以用super.fieldName。

public class Main {
    public static void main(String[] args) {
        Student s = new Student("Xiao Ming", 12, 89);
    }
}

class Person {
    protected String name;
    protected int age;
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
}

class Student extends Person {
    protected int score;
    public Student(String name, int age, int score) {
        this.score = score;
    }
}

运行上面的代码,会得到一个编译错误.因为 public Student(String name, int age, int score) 没有明确地调用父类的构造方法,编译器会帮我们自动加一句super();
所以,Student类的构造方法实际上是

class Student extends Person {
    protected int score;
    public Student(String name, int age, int score) {
        super(); // 自动调用父类的构造方法
        this.score = score;
    }
}

但是,Person类并没有无参数的构造方法 Person(); ,因此,编译失败。
解决方法是调用Person类存在的某个构造方法。例如:

class Student extends Person {
    protected int score;

    public Student(String name, int age, int score) {
        super(name, age); // 调用父类的构造方法Person(String, int)
        this.score = score;
    }
}

子类型可以安全地变为更加抽象的父类型,把一个子类类型安全地变为父类类型的赋值,被称为向上转型(upcasting)。

Student s = new Student();
Person p = s; // upcasting, ok
Object o1 = p; // upcasting, ok
Object o2 = s; // upcasting, ok

抽象的父类型想要变为更加具体的子类,容易失败。把一个父类类型强制转型为子类类型,就是向下转型(downcasting)。为了避免向下转型出错,Java提供了instanceof操作符,可以先判断一个实例究竟是不是某种类型。
详情点击此处向下转型

示例
定义PrimaryStudent,从Student继承,并新增一个grade字段。

public class Main {
    public static void main(String[] args) {
        Person p = new Person("小明", 12);
        Student s = new Student("小红", 20, 99);
        // TODO: 定义PrimaryStudent,从Student继承,新增grade字段:
        Student ps = new PrimaryStudent("小军", 9, 100, 5);
        System.out.println(ps.getScore());
    }
}

class Person {
    protected String name;
    protected int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() { return name; }
    public void setName(String name) { this.name = name; }

    public int getAge() { return age; }
    public void setAge(int age) { this.age = age; }
}

class Student extends Person {
    protected int score;

    public Student(String name, int age, int score) {
        super(name, age);
        this.score = score;
    }

    public int getScore() { return score; }
}

class PrimaryStudent extends Student{
	// TODO
	protected int grade;
	public PrimaryStudent(String name, int age, int score, int grade){
		super(name, age, score);
		this.grade = grade;
	}
	public int getGrade() { return grade; }
}

多态

在继承关系中,子类如果定义了一个与父类方法签名完全相同的方法,被称为覆写(Override)。
加上@Override可以让编译器帮助检查是否进行了正确的覆写。希望进行覆写,但是不小心写错了方法签名,编译器会报错。
多态是指针对某个类型的方法调用,其真正执行的方法取决于运行时期实际类型的方法。
见笔记。

class Person {
    public void run() {}
}

class Student extends Person {
    @Override
    public void run() {}
}

class Teacher extends Person {
    @Override
    public void run() {}
}

覆写Object方法
因为所有的class最终都继承自Object,而Object定义了几个重要的方法:

  • toString():把instance输出为String;
  • equals():判断两个instance是否逻辑相等;
  • hashCode():计算一个instance的哈希值。
    在必要的情况下,我们可以覆写Object的这几个方法。

调用super
在子类的覆写方法中,如果要调用父类的被覆写的方法,可以通过super来调用。

final
继承可以允许子类覆写父类的方法。如果一个父类不允许子类对它的某个方法进行覆写,可以把该方法标记为final。用final修饰的方法不能被Override。

  • final修饰的方法可以阻止被覆写;
  • final修饰的class可以阻止被继承;
  • final修饰的field必须在创建对象时初始化,随后不可修改。

抽象类

如果一个class定义了方法,但没有具体执行代码,这个方法就是抽象方法,抽象方法用abstract修饰。
因为无法执行抽象方法,因此这个类也必须申明为抽象类(abstract class)。必须把Person类本身也声明为abstract,才能正确编译它。
使用abstract修饰的类就是抽象类。我们无法实例化一个抽象类,抽象类可以强迫子类实现其定义的抽象方法,否则编译会报错。因此,抽象方法实际上相当于定义了“规范”。

public class Main {
    public static void main(String[] args) {
        Person p = new Student();
        p.run();
    }
}

abstract class Person {
    public abstract void run();
}

class Student extends Person {
    @Override
    public void run() {
        System.out.println("Student.run");
    }
}

接口

如果一个抽象类没有字段,所有方法全部都是抽象方法,就可以把该抽象类改写为接口interface。

所谓interface,就是比抽象类还要抽象的纯抽象接口,因为它连字段都不能有。因为接口定义的所有方法默认都是public abstract的,所以这两个修饰符不需要写出来(写不写效果都一样)。

当一个具体的class去实现一个interface时,需要使用implements关键字。

在Java中,一个类只能继承自另一个类,不能从多个类继承。但是,一个类可以实现多个interface。

class Student implements Person, Hello { // 实现了两个interface
    ...
}

一个interface可以继承自另一个interface。interface继承自interface使用extends,它相当于扩展了接口的方法。例如:

interface Hello {
    void hello();
}

interface Person extends Hello {
    void run();
    String getName();
}

此时,Person接口继承自Hello接口,因此,Person接口现在实际上有3个抽象方法签名,其中一个来自继承的Hello接口。

default方法

静态字段和静态方法

在一个class中定义的字段,我们称之为实例字段。实例字段的特点是,每个实例都有独立的字段,各个实例的同名字段互不影响。

还有一种字段,是用static修饰的字段,称为静态字段:static field。

实例字段在每个实例中都有自己的一个独立“空间”,但是静态字段只有一个共享“空间”,所有实例都会共享该字段。举个例子:

class Person {
    public String name;
    public int age;
    // 定义静态字段number:
    public static int number;
}

不推荐用 实例变量.静态字段 去访问静态字段,因为在Java程序中,实例对象并没有静态字段。在代码中,实例对象能访问静态字段只是因为编译器可以根据实例类型自动转换为 类名.静态字段 来访问静态对象。

推荐用 类名 来访问 静态字段。可以把静态字段理解为描述class本身的字段(非实例字段)。

Person.number = 99;
System.out.println(Person.number);

用static修饰的方法称为静态方法。

调用实例方法必须通过一个实例变量,而调用静态方法则不需要实例变量,通过类名就可以调用。静态方法类似其它编程语言的函数。

因为静态方法属于class而不属于实例,因此,静态方法内部,无法访问this变量,也无法访问实例字段,它只能访问静态字段。

通过实例变量也可以调用静态方法,但这只是编译器自动帮我们把实例改写成类名而已。

通常情况下,通过实例变量访问静态字段和静态方法,会得到一个编译警告。

静态方法经常用于工具类。例如:

Arrays.sort()

Math.random()

静态方法也经常用于辅助方法。注意到Java程序的入口main()也是静态方法。

接口的静态字段
因为interface是一个纯抽象类,所以它不能定义实例字段。但是,interface是可以有静态字段的,并且静态字段必须为final类型:

public interface Person {
    public static final int MALE = 1;
    public static final int FEMALE = 2;
}

实际上,因为interface的字段只能是public static final类型,所以我们可以把这些修饰符都去掉,上述代码可以简写为:

public interface Person {
    // 编译器会自动加上public statc final:
    int MALE = 1;
    int FEMALE = 2;
}

编译器会自动把该字段变为public static final类型。

真正的完整类名是 包名.类名

在Java虚拟机执行的时候,JVM只看完整类名,因此,只要包名不同,类就不同。

包可以是多层结构,用.隔开。例如:java.util。

要特别注意:包没有父子关系。java.util和java.util.zip是不同的包,两者没有任何继承关系。

位于同一个包的类,可以访问包作用域的字段和方法。

作用域

  • 定义为public的class、interface可以被其他任何类访问;
  • 定义为private的field、method无法被其他类访问;
  • protected作用于继承关系。定义为protected的字段和方法可以被子类访问,以及子类的子类;

如果不确定是否需要public,就不声明为public,即尽可能少地暴露对外的字段和方法。

把方法定义为 package权限 有助于测试,因为测试类和被测试类只要位于同一个package,测试代码就可以访问被测试类的package权限方法。

一个.java文件只能包含一个public类,但可以包含多个非public类。如果有public类,文件名必须和public类的名字相同。

classpath和jar

了解了解,详情见classpath和jar

模块

了解了解,详情见模块

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值