涉及知识点:类、继承、接口
参考书籍:
Java 2 实用教程——耿祥义 张跃平
Java语言程序设计——梁勇【著】戴开宇【译】
类与对象
基础
- 一个文件只能有一个公共类,公共类必须与文件同名
- 包含
mian
方法的叫主类
构造方法
特点
- 和所在类同名
- 没有返回值类型,没有void,只可以有权限修饰符。
- 作用:初始化对象
- 可重载(重载见下)
实例成员与类成员
实例变量与类变量:
-
实例变量
float x;
-
类变量也叫static变量、静态变量
static int y;
- 所有对象共享类变量:当创建多个不同对象时,分配给这些对象的类变量占有相同的一处空间,改变其中一个对象的类变量就会影响其他对象的这个类变量。
- 类变量可以通过对象访问,也可以通过类访问
- 注意: 不能用static修饰局部变量。
实例方法与类方法:
-
实例方法
int max(int a, int b){ return a>b?a:b; }
-
类方法
static void outPut(String s){ System.out.println(s); }
-
两者的区别:
类方法可以被对象调用,也可以直接通过类名调用。实例方法只能通过对象调用。
类方法不可以操作实例变量
-
涉及类方法的原则:
一个方法不需要操作类中的任何实例变量,或调用类中的实例方法
-
一些类方法,以Arrays为例
Arrays.sort
public static void sort(double a[])
Arrays.binarySearch
public static int binarySearch(double[]a, double number)
批注: a 必须是已经排序好的数组。若number在数组中,返回索引;不在,返回负数。
方法重载-overload
一个类中可以有多个方法具有相同的名字,但这些方法的参数必须不同。
float hello (int a, int b){
return a+b;
}
float hello (double a, double b){
return a+b;
}
this 关键字
this引用数据域
class A{
private double radius;
public void setRadius(double radius){
this.radius = radius;
}
}
this调用构造方法
public class Circle{
private double radius;
public Circle(double radius){
this.radius = radius; // 引用数据域
}
public Circle(){
this(1.0); // this关键字调用第一个构造方法
}
}
访问权限
提前注释:类中的实例方法总是可以操作该类中的实例变量和类变量;类方法总是可以操作该类中的类变量,与访问权限符无关。
私有变量与私有方法—private
无法在另一个类中,直接通过 . 来访问这个类中的私有变量与私有方法,可以设置setter 与 getter 的public方法来修改与访问。
class Student{
private int age;
public void setAge(int age){
this.age = age;
}
public int getAge(){
return age;
}
}
public class void mian(String[] ages){
Student student1 = new student();
student1.setAge(18);
System.out.println("his age is " + student1.getAge());
// 这里getAge才能得到年龄,无法通过student1.age来访问。
}
公有变量和公有方法—public
任何类都可以访问该类中的公有变量与公有方法。
受保护的成员变量和方法—protected
class tom{
protected double weight;
protected double sum_(double a, double b){
return a+b;
}
}
当在另一个类中用tom类创建一个对象后,如果这个类与tom类在一个包中,那么该对象能访问自己的protected变量和方法,也可以通过类名才访问。
在UML中,protected用#表示
友好变量与友好方法
不用public、protected、private修饰的是友好的
当在另一个类中用tom类创建一个对象后,如果这个类与tom类在一个包中,那么该对象能访问自己的友好变量和友好方法,也可以通过类名才访问。
向方法传递对象参数
给方法传递一个对象,就是将对象的引用传递给方法
class Circle{
……
}
public class Test{
public static void printCircle(Circle c){ // 这个就是向方法传递对象参数
System.out.println("圆的面积是"+ c.getAera());
}
public static void main(String[] args){
Circle myCircle = new Circle(5.0);
printCircle(myCircle);
}
}
对象数组
Student[] stu = new Student[10]; // 创建对象数组
stu[0] = new Student(); // 创建Student对象stu[0]
……
Date类
import java.util.Date;
Date date = new Date();
System.out.Println("从1970零时零分至今,一共多少毫秒:"+date.getTime());
System.out.Println("现在的时间:"+ date); //或者 date.toString()
一般在构造函数的时候声明对象并赋值。
import java.util.Date
class loan{
private double annualInterestRate,loanAmount;
private int numberOfYears;
private Date loanDate;
public loan(double annualInterestRate,int numberOfYears,Date loanDate){
this.annualInterestRate = annualInterestRate;
this.numberOfYears = numberOfYears;
loanDate = new Date();
}
}
继承和多态
父类与子类
extents 定义字类:class Eafan extends People{ }
Object类是所有类的祖先类。
一个子类只能有一个父类,一个父类可以有多个子类
子类也可以有子类—子孙类
子类的继承性
子类继承父类的成员变量作为自己的一个成员变量,可以被子类中自己定义的任何实例方法操作。
子类继承父类的方法作为子类的一个方法,可以被子类中自己定义的任何实例方法调用。
在同一个包中的继承性
private 成员变量和方法不可继承,其他的都可以,继承过来的成员变量和方法本身的访问权限保持不变。
不再同一个包中的继承性
private和友好的成员变量和方法不可继承,只能继承protected 和 public。
protected说明
若 B 类是 A 类的子类(子孙类),C 类和 A 类在同一个包中,则在 C 类中创建 B 类的 b 对象后,b 对象可以使用 B 类所继承的 A 类的受保护成员变量和调用 B 类所继承的 A 类的受保护方法。
子类与对象
可以操作的对象
子类继承的方法只能操作继承过来的成员变量或未继承的成员变量,不可以操作子类新申明的变量。
子类对象的生成
-
子类创建对象时,子类的构造方法总是先调用父类的某个构造方法,完成父类部分的创建;然后再调用子类自己的构造方法,完成子类部分的创建。
-
如果子类的构造方法没有明显地指明使用父类的哪个构造方法,子类就调用父类的不带参数的构造方法 。
-
如果父类没有不带参数的构造方法,则在子类的构造方法中必须明确的告诉调用父类的某个带参数的构造方法,通过
super
关键字,这条语句还必须出现在构造方法的第一句。 -
所以,如果要设计一个可继承的类,最好提供一个无参构造方法,以避免程序设计错误。
注:代码可见下 super 中 父类与子类的构造方法
instanceof
instanceof运算符是Java独有的双目运算符,其左面的操作元是对象,右面的操作元是类,当左面的操作元是右面的类或其子类所创建的对象时,instanceof运算的结果是true,否则是false.
eg: student1 instanceof People
Super关键字
super 指代父类,可以用于调用父类中的普通方法和构造方法
调用父类中的构造方法
父类的构造方法不可以被子类继承,只能使用super从子类的构造方法中调用
// 调用无参构造方法
super();
// 调用有参构造方法
class GeometricObject{
private String color;
private boolean filled;
private Date dateCreated;
public GeometricObject(){ // 见上 — 子类对象的生成,无参构造会为子类的构造创造更多选择
dateCreated = new Date();
}
public GeometricObject(String color, boolean filled){
dateCreated = new Date();
this.color = color;
this.filled = filled;
}
}
public Circle extends GeometricObject{
private radius;
public Circle(){
}
public Circle(double radius){
this.radius = radius;
}
public Circle(double radius, String color, boolean filled){
super(color,filled); // super 必须放在第一行
this.radius = radius;
}
}
调用父类的普通方法
见下方的 .方法重写 (ps:ctrl+左击➡ 跳转)
调用隐藏的成员变量和方法
在子类中想使用被子类隐藏的成员变量或方法就可以使用关键字super
class Sum {
int n;
float f() {
float sum=0;
for(int i=1;i<=n;i++)
sum=sum+i;
return sum;
}
}
class Average extends Sum {
int n;
float f() {
float c;
super.n = n;
c = super.f();
return c/n;
}
float g() {
float c;
c = super.f();
return c/2;
}
}
public class Example5_7 {
public static void main(String args[]) {
Average aver=new Average();
aver.n=100;
float resultTwo=aver.g(); float resultOne=aver.f();
System.out.println("resultOne="+resultOne);
System.out.println("resultTwo="+resultTwo);
}
}
成员变量的隐藏和方法重写
成员变量的隐藏
隐藏:对于子类可以从父类继承的成员变量,只要子类中声明的成员变量和父类中的成员变量同名时,子类就隐藏了继承的成员变量。
- 子类对象以及子类自己定义的方法操作这个与父类同名的成员变量时,子类操作的是子类重新声明的成员变量,不是被隐藏掉的。
- 子类继承的方法所操作的成员变量是被子类继承的或隐藏的成员变量(在父类中声明的)。
方法重写
实质:子类通过重写,隐藏已继承的实例方法
方法重写:子类中定义一个方法,这个方法的类型和父类的方法的类型一致或是父类方法的类型的子类型,且这个方法的名字、参数个数、参数的类型和父类的方法完全相同。
重写方法不可以直接使用被子类隐藏的父类(祖先类)的成员变量和直接调用被子类隐藏的父类(祖先类)的方法。
重写方法的访问权限可以升但不可以降。 访问权限:共有>受保护>友好>私有
// 常常重写 toString()方法
class GeometricObject{
private String color;
private boolean filled;
private Date dateCreated;
……
public String toString(){
return "created on " + dateCreated + "\ncolor: " + color + "and filled: " + filled;
}
}
class Circle extends GeometricObject{
// other methods are omitted
@Override
public String toStirng(){
return super.toString + "\nradius is: " + raidus;
}
}
解读:
- toString 方法在GeometricObject类中定义并在Circle类中修改。
- 两个方法都可以在Circle中使用。调用父类的,用super.toString( )。
- @override——重写标注。该标注表示被标注的方法必须重写父类的一个方法。如果没有重写,会报错。有利于找到错误。
注意:
- 仅当实例方法可以被访问时,才可以被重写。因为私有方法在它自身类之外是不能访问的,所以它不能被重写。
- 如果子类中定义的方法在父类中是私有的,那么这两个方法完全没有关系。
- 与实例方法一样,静态方法也能被继承,但不能被重写。
- 需要与方法重载进行区分。
Final关键字
final关键字可以修饰类、成员变量、方法中的局部变量。
final 类
final类不能被继承,即不能有子类。
final方法
用final修饰父类地一个方法,这个方法不允许被子类重写。(老老实实地继承,不可以篡改)
final常量
成员变量、局部变量被final修饰,则就是常量。
程序在声明常量时必须指定该常量值。
上转型对象
Animal 类是Tiger 的父类
Animal a;
Tiger b = new Tiger();
a = b;
a 为b 的上转型对象。
本质:子类创建的对象的引用放到了父类的对象中
上转型对象会失去原对象的一些属性和功能:
- 不能访问子类新增的成员变量
- but 可以访问子类继承或隐藏的成员变量,也可以调用子类继承的方法或子类重写的实例方法
区分:
-
如果子类重写了父类的静态方法,那么子类对象的上转型对象不能调用子类重写的静态方法,只能调用父类的静态方法。
-
如果子类重写了父类的某个实例方法,当对象的上转型对象调用这个实例方法时一定是调用了子类重写的实例方法。
注:可以将上转型对象强制转化成子类对象,则又会具备子类所有属性和功能
class monkey{
void crySpeak(String s){
System.out.println(s);
}
}
class people extends monkey{
@Override
void crySpeak(String s){
System.out.println("***"+s+"***");
}
}
public class test{
public static void main(String[] args){
monkey monkey1;
people fu = new people();
monkey1 = fu; // 上转型对象
monkey.crySpeak("嗨害嗨"); // 结果是:***嗨害嗨***,不是嗨害嗨
people fu = (people)monkey1; //强制转换为people类
}
}
abstract类、abstract方法
在UML图中,抽象类、抽象方法用斜体表示
abstract方法
- 只允许声明,没有方法体
- 不可以用final和abstract修饰同一个方法或类
- 不可以使用static和private修饰abstract
- abstract方法必须是非private的实例方法(权限必须高于private)
abstract类
- 只有abstract类可以有abstract方法
- abstract类的构造方法定义为protected, 因为只被子类调用。
- 可以使用abstract类生命声明对象,不能用new标识符创建abstract类的对象,但可以成为子类对象的上转型对象,可以调用子类重写的方法(子类根据抽象类中的行为标准给出具体行为)
- 如果一个非抽象类是抽象类的子类,那么它必须重写父类的抽象方法,给出方法体。(父类的抽象方法在子类的UML图中通常省略)
- 如果一个抽象类是抽象类的子类,可以重写父类的抽象方法,也可以继承父类的抽象方法。
- 解释:找女朋友,需要其具备speak( ), cooking( )行为,但不可以给出speak( ),cooking( )的具体细节。
// 这是上转型与抽象的代码例子
abstract class Animal{
// 只允许声明,没有方法体
abstract public void cry();
abstract public String getAnimalName();
}
class Dog extends Animal{
//如果一个非抽象类是抽象类的子类,那么它必须重写父类的抽象方法,给出方法体。
@Override
public void cry() {
System.out.println("汪汪");
}
@Override
public String getAnimalName(){
return "我是狗狗";
}
}
class Cat extends Animal{
//如果一个非抽象类是抽象类的子类,那么它必须重写父类的抽象方法,给出方法体。
@Override
public void cry() {
System.out.println("喵喵");
}
@Override
public String getAnimalName(){
return "我是猫猫";
}
}
class Simulator{
// 可以使用abstract类生命声明对象,不能用new标识符创建abstract类的对象,但可以成为子类对象的上转型对象,可以调用子类重写的方法
public void playSound(Animal animal){
System.out.println(animal.getAnimalName());
animal.cry();
}
}
public class Application {
public static void main(String[] args){
Simulator simulator = new Simulator();
simulator.playSound(new Dog());
simulator.playSound(new Cat());
}
}
接口与实现
接口
作用:用于定义类的对象的共同行为(包括不相关的类)。例如指明这些对象是可比较的、可食用的、可克隆的……
使用 interface
来定义接口:
interface A{
// 接口体
}
在java中,接口被看作一种特殊的类。与抽象类类似,不能使用new操作符创建接口的实例。
接口中的方法访问权限只有private 和 public
访问权限:
-
public interface ……是public接口,public接口可以被任何一个类实现。
-
不加public 修饰的接口是友好接口,可以被与该接口在同一个包中的类实现
接口可以继承
一个源文件中可以包含若干类和若干接口
接口体中的抽象方法和常量
抽象方法的访问权限一定是public ,允许省略public abstract
修饰符
所有static常量的常量的访问权限一定是public,并且允许省略public static final
修饰符
接口体中无变量
interface Com{
int Max = 100; // public static final int Max = 100;
void add(); // public abstract void add();
float sum(float x, float y); // public abstract float sum(float x, float y); 抽象方法没有方法体
}
接口体中的default实例方法
default的实例方法的访问权限必须是public(public可省略)
public default int max(int a, int b) {
return a > b ? a:b;
}
不可以省略default关键字,因为在接口体中不允许定义通常的带方法体的public实例方法。
接口体中的static方法
public static void f(){
System.out.println("yeyeye");
}
接口体中的private方法
目的:配合接口中的default实例方法。
算法封装在private方法中,供接口中的default实例方法调用
接口实现
-
接口由类来实现,以便使用接口中的方法。
-
一个类需要在类声明中使用关键字implements声明该类实现一个或多个接口。若实现多个接口,用逗号隔开接口名
class A implements Com, Addable
class Dog extends Animal implements Eatable,Sleepable
- 如果父类实现了接口,子类就自然地实现了接口,不用显示地使用关键字implements声明
重写接口中的方法
一个类实现了某个接口:
-
那么这个类就自然拥有了接口中的常量、default方法
-
不拥有接口的static 和 private 方法
-
该类需要重写接口中的default方法,重写时去掉default关键字
-
该类是非abstract类,则必须重写该接口中所有abstract方法(去掉abstract, 给出方法体)
-
该类是abstract类,可以重写abstract方法或者直接调用
-
重写接口方法不可以省略public
使用接口中的常量和static方法
接口名.常量
接口名.静态方法
接口回调
实质:把实现某一接口的类创建的对象的引用赋值给该接口声明的接口变量,那么该接口变量就可以调用被类实现的接口方法以及接口提供的default方法或类重写方法。(接口变量调用被类实现的接口方法时就是通知相应的对象调用这个方法)
非常类似上转型对象调用子类重写的方法
interface ShowMessage{
void showBrand(String s); // 抽象方法
default void outPutStart(){
System.out.println("**********");
}
}
class TV implements ShowMessage{
public void showBrand(String s){
System.out.println("**********"+s+"tvtvtv");
}
}
class PC implements ShowMessage{
public void showBrand(String s){
System.out.println("**********"+s+"pcpcpc");
}
}
public class Example{
public static void main(String[] args){
ShowMessage sm; // 声明接口变量
sm = new TV(); // 接口的类创建的对象赋值给接口变量
sm.showBrand("长城牌电视机"); // 接口回调, 调用被类重写的方法
sm = new PC(); // 同理
sm.showBrand("huawei");
}
}
注: 文章仅仅是对两本书的汇总整理,代码大部分来源于这两本书。