面向对象
一、面向对象基础知识
面向对象:从现实世界中客观存在的事物(即对象)出发构造软件系统,尽可能运用人类的自然思维方式,使计算机语言对事物的描述和现实世界该事物的本来面目一致。 类、对象就是面向对象方法的核心观念。
类:具有相同属性和服务/行为的一组对象的集合。内部包括属性和行为。 对象:描述客观事物的一个实体,构成系统的一个基本单位。由一组属性和操作这一属性的行为组成。 类的实例化是对象,一组对象的抽象是类。
Java语言中,类是一种最基本的复合数据类型,是组成Java程序的基本要素。 Java类包含变量定义(对象的属性)和方法定义(对象的行为/操作) Java类支持面向对象(OOP)三个基本特征:封装、继承、多态。
封装:把对象的属性和服务结合成一个独立的相同单位,隐蔽对象的内部细节。
继承:特殊类的对象拥有其一般类的全部属性与操作,称为特殊类对一般类的继承。Java原因只支持单继承,不允许多继承,但可以使用接口实现模拟多继承。
多态:一般类被特殊类继承后,可以具有不同的数据类型或表现出不同的行为。多态的概念比较简单, 就是同一操作作用于不同的对象, 可以有不同的解释, 产生不同的执行结果。 多态性有静态多态和动态多态,分别用方法重载和方法重写。 静态多态-运行:有类继承或者接口实现、 子类要重载父类的方法、 父类的引用指向子类的对象。 动态多态-编译: 子类要重写父类的方法。
//例题3_1声明一个Person类
class Person { // 定义类Person
private String name; // 私有成员变量,表示姓名
public void setName(String n) { // 公有方法
name = n;
}
public String getName() { //公有方法
return name;
}
}
二、类的定义
定义类就是创建一个新的数据类型,利用定义的类可以定义类实例-创建一个对象。
//例题3_2为Person类声明一组成员变量
class Person1 { // 定义类Person
String name; // 定义成员变量name
char sex; // 定义成员变量sex
int age; // 定义成员变量age
}
1. 类定义格式及说明
类相当于面向过程语言中的结构体
//成员变量
[public | protected | default | private] [static] [final] [transient] [volatile] type variableName;
//成员方法
[public | protected | default | private] [static] [final | abstract] [native] [synchronized] returnType methodName(paramList) [throws exceptionList]
{
//方法主体
Statements
}
}
## 2. 类的成员变量
成员变量定义了类的属性。
不对成员变量赋初值,系统会自动赋默认值。
## 3. 类的成员方法
成员方法描述了对象所具有的功能或操作。相当于过去所说的子程序、函数。
```java
//例题3_3为Person类声明一个成员方法
class Person3 { // 定义类Person
String name; // 定义成员变量name
char sex; // 定义成员变量sex
int age; // 定义成员变量age
// 定义方法sayHello
public void sayHello() {
System.out.println("Hello!"); // 在控制台打印"Hello!"
}
}
三、对象的创建
1. 创建对象
声明格式为:类名 对象名=new 类名()
2. 访问对象的属性和行为
使用格式为:对象名.对象成员 同类的对象可以进行赋值,称为对象赋值。
//例题3_4设计类Number,测试对象间的赋值。
public class Number {
int i;
public static void main(String[] args) {
Number n1 = new Number();
Number n2 = new Number();
n1.i = 9;
n2.i = 47;
System.out.println("n1.i=" + n1.i + "\t\t" + "n2.i=" + n2.i);
n1 = n2; //通过对象赋值后,n1 n2其实是一个对象,所以改变一个,另一个自动跟着变。
System.out.println("n1.i=" + n1.i + "\t\t" + "n2.i=" + n2.i);
n1.i = 27;
System.out.println("n1.i=" + n1.i + "\t\t" + "n2.i=" + n2.i);
}
}
四、类的访问修饰符
Java语言中访问权限控制符(访问控制修饰符)有四个:public、protected、default、private。定义类时,访问控制修饰符只有一个public。
每个Java程序的主类必须是public类,主类名=文件名。子类没有public,[其他修饰符] class 类名。
访问控制修饰符:主要用于定义类及其成员的作用域。如在哪些范围内访问类及其成员。 类型说明符:主要用于定义类及其成员的一些特殊性质。如是否可以被修改。
能修饰类class的修饰符有:
[public] [abstract | final] public:用public修饰的类或成员拥有公共作用域。 abstract:抽象类,不能被new,没有方法体。 final:方法不能被重写。
能修饰成员变量的修饰符有:
public:用public修饰的类或成员,拥有公共作用域。 protected:用protected修饰的类或成员,拥有受保护作用域,只能被同一个包中所有类及其子类访问。 default:只能被同一包同一类中访问。 private:用private修饰的类或成员,拥有私有作用域,只能在此类中访问。 static:静态变量(类变量),相对于实例变量。 final:常量,其值不能被改变。 transient:暂时性变量,用于对象存档。 volatile:贡献变量,用于并发线程的共享。在原子操作的情况下,当变量被volatile修饰时,线程每次使用时都会直接到内存中提取,不会去缓存
## 能修饰成员方法的修饰符有:
[public | protected | default | private] [static] [final | abstract] [native] [synchronized]
public:用public修饰的类或成员,拥有公共作用域。
protected:用protected修饰的类或成员,拥有受保护作用域,只能被同一个包中所有类及其子类访问。
default:只能被同一包同一类中访问。
private:用private修饰的类或成员,拥有私有作用域,只能在次类中访问。
static:类方法,可以通过类名直接调用。
final:方法不能被重写。
abstract:抽象方法,没有方法体。
native:集成其他语言的代码。
synchronized:控制多个并发线程的访问 实现同步。
```java
//例题3_5测试成员变量修饰符的作用
//FieldTest类,子类,没有任何修饰符,默认访问权限,可以在同包下其他类访问
class FieldTest {
//private int num = 5;// 私有作用域,本类可见
//public int num = 5;// 工共作用域,所有包/类可见
protected int num = 5;// 受保护作用域,同包/同类/不同类可见
public int get() { // 公共作用域,get 方法返回成员变量num的值
return num;
}
}
//主类,每个Java程序的主类必须是public,主类名=文件名
public class Example3_5 {
//子类中的输出类-静态方法
public static void main(String[] args) {
FieldTest ft = new FieldTest();
int t = ft.get(); // 正确访问
int s=ft.num; //不能访问FieldTest类中私有成员变量num
System.out.println("t=" + t);
System.out.println("s="+s);
}
}
五、访问权限
访问控制修饰符:主要用于定义类及其成员的作用域。如在哪些范围内访问类及其成员。 没有使用任务修饰符的,拥有默认访问权限(友好访问权限),可以被同包下的其他类访问。 成员的作用范围受到类的作用范围的限制。 缺省=deault默认范围
六、static修饰符
使用static修饰的变量和方法,称为类变量(静态变量)和类方法(静态方法)。没有用,称为实例变量和实例方法。
类成员(静态成员)属于这个类(即类中所有对象共同拥有),实例成员由每个独享个体独有。
对于实例成员:只能通过对象来访问,不能通过类名进行访问。 对于类成员:在静态方法中,只能调用同类中其他静态成员(变量和方法),不能直接访问类中的非静态成员。在实例方法中,即可访问实例成员,也可访问类成员。
成员变量中必须有类型和变量名,成员方法中必须有类型和方法名(),类中必须有class和类名。
为什么main是static修饰? static静态方法是存储在静态存储区内的,可以通过类.方法名直接进行调用,不需要进行实例化。 假设不使用static,那么main()方法在调用时必须先对其实例化,而main()做为程序的主入口显然不可能先对其实例化, 所以使用static修饰,可以更方便的直接用类.main()对其调用。
//例题3_6测试对实例成员和类成员的不同访问方式——静态变量使用示例
// Count子类 没有任何修饰符,默认访问权限,可以在同包下其他类访问
class Count {
private int serial; // 实例变量
static int counter = 0; // 类变量
/**成员变量中必须有类型和变量名,成员方法中必须有类型和方法名(),类中必须有class和类名*/
// //Count(){}=语句块功能
// Count() {
// counter++; // 实例计数器
// serial = counter;
// }
//语句块{}代替
{
counter++; // 实例计数器
serial = counter;
}
int getSerial() {
return serial;
}
int getCounter() {
return counter;
}
}
public class Example3_6 {
//在类方法(静态方法)中,不能直接访问类中的非静态成员
//为什么main方法必须是static,因为main方法是程序入口点,执行main方法,此时是没有对象的。
//因此main方法不可能是实例方法
// static静态方法是存储在静态存储区内的,可以通过类.方法名直接进行调用,不需要进行实例化。
// 假设不使用static,那么main()方法在调用时必须先对其实例化,而main()做为程序的主入口显然不可能先对其实例化,
// 所以使用static修饰,可以更方便的直接用类.main()对其调用。
public static void main(String args[]) {
Count c1 = new Count();
Count c2 = new Count();
System.out.println("c1的serial值:" + c1.getSerial()); // 1
System.out.println("c1的counter值:" + c1.getCounter());
System.out.println("c2的sSerial值:" + c2.getSerial()); // 2
System.out.println("c2的counter值:" + c2.getCounter());
// System.out.println("类的serial值:"+Count.serial()); //不能通过类名访问非静态变量
System.out.println("类的counter值:" + Count.counter); // 通过类名访问静态变量
}
}
//例题3_7测试类变量与实例变量的使用区别
public class Example3_7 {
int i = 0; //实例变量
static int j = 0; //类变量(静态变量) 是等程序结束时才释放静态存储区。
public void print() {
System.out.println("i=" + i);
System.out.println("j=" + j);
}
public static void main(String[] args) {
Example3_7 sv1 = new Example3_7();
sv1.i++;
sv1.j++;
sv1.print();
Example3_7 sv2 = new Example3_7();
sv2.print();
}
}
七、方法重载
同一个类中的两个及以上的方法可以有同一名字,只要它们参数声明不同即可。该方法称为重载,该过程称为方法重载。
作用:在一个类中,多个方法同名不同参数列表,实现多个不同的功能。
八、构造方法
每个类都有构造方法,它是类的一种特殊方法,构造方法的名字和类名字完全相同。分为无参构造/有参构造。 对象的创建是通过构造方法完成的,当类实例化一个对象时,类会自动调用构造方法。
基本特点:没有返回值,不用viod。名称=类名。方法可以重载。不能由用户直接调用,只能使用new对象时系统自动调用。
类的构造方法不要求必须定义。没有明确时,Java会自动默认构造一个无参构造方法。
//例题3_8方法重载示例
public class Example3_8 {
public static void print(String str) {
System.out.println("String=" + str);
}
public static void print(int i) {
System.out.println("int=" + i);
}
public static void print(float i) {
System.out.println("float=" + i);
}
public static void main(String[] args) {
print("123"); // 通过方法名,直接调用类方法
print(123);
print(1.23f);
}
}
//例题3_9构造方法声明示例
public class Point2D {
private int x;
private int y;
// 定义无参构造
public Point2D() {
}
// 定义带两个参数的构造方法
public Point2D(int x, int y) {
this.x = x;
this.y = y;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
}
//例题3_10定义一个水果类——类的构造方法示例
public class Fruit {
public String color; // 定义颜色成员变量
// 定义构造方法
public Fruit() {
color = "绿色"; // 对变量color进行初始化
}
public void harvest() { // 定义收获的方法 重载构造方法
String color = "红色"; // 定义颜色局部变量
System.out.println("水果是:" + color + "的!");
System.out.println("水果已经收获……");
System.out.println("水果原来是:" + this.color + "的!");
}
public static void main(String[] args) {
// 声明Fruit类的一个对象fruit,并为其分配内存 对象声明-对象实例化
Fruit myfruit = new Fruit();
myfruit.harvest(); // 调用Fruit类的harvest()方法
}
}
九、关键字this
this关键字代表引用自身对象,访问本类的成员变量和方法。 语句格式: this.成员变量名 this.方法名(实参列表)
1. 使用this关键字引用成员变量
参数a的作用范围为构造方法或方法内部。成员变量a的作用范围时类内部。 当变量作用范围重叠时,作用范围小的覆盖作用范围大的。 如果变量名不发生重叠,可以省略this。
//例题3_11this引用成员变量
public class ThisDemo1 {
private int a; //定私有作用域的成员变量a
public ThisDemo1(int a) {
this.a = a; // this.a表示引用该类的成员变量
}
public int getA() {
return a;
}
public void setA(int a) {
this.a = a;
}
// 参数a的作用范围为构造方法或方法内部。成员变量a的作用范围时类内部。
// 当变量作用范围重叠时,作用范围小的覆盖作用范围大的。
// 如果变量名不发生重叠,可以省略this。
}
2. 使用this关键字在自身构造方法内部引用其他构造方法
this(0)调用本类中的其他构造方法,其中0是根据需要传递的参数类型的值。 调用的代码只能出现在自身构造方法内部的第一行。这样使用最多只有一次。
//例题3_12this引用构造方法
public class ThisDemo2 {
int a;
int b;
public ThisDemo2() {
this(0,'0'); // this(0)调用本类中的其他构造方法
}
public ThisDemo2(int a) {
this.a = a;
}
public ThisDemo2(int a,int b) {
this.a = a;
this.b = b;
}
public ThisDemo2(int a,char b) {
this.a = a;
this.b = b;
}
}
3. 使用this关键字代表自身类的对象
this代表自身对象
4. 使用this关键字引用成员方法
this引用成员方法 this.方法名(参数)
//例题3_13this引用自身和成员方法
public class ThisDemo3 {
ThisDemo3 instance; //定义声明对象 创建一个对象=实例化/初始化一个类
public ThisDemo3() {
instance = this; // this代表自身对象
this.test(); // this引用成员方法
}
public void test() {
System.out.println(this); //输出自身对象
}
public static void main(String[] args) {
ThisDemo3 thisDemo3=new ThisDemo3(); //在实例化过程中,自动调用无参/有参构造
}
}
十、继承的概念
定义:类继承又称类派生。当B类继承A类时,B类具有A类的全部属性和方法,又具有自己特有的属性和方法。 被继承类A:基类、父类、超类。 继承类B:派生类、子类。
//圆的子类Circle
public class Circle extends Shape {
double radius; // 定义自己的成员变量
public double getRadius() { // 定义自己的成员方法
return radius;
}
public void setRadius(double radius) {
this.radius = radius;
}
public double getArea() {
double area = Math.PI * radius * radius; // 计算圆的面积
return area;
}
public double getPerimeter() {
return 2 * Math.PI * radius; // 计算并返回圆的周长
}
}
//父类图形类Shape
public class Shape {
String type; // 类别
public void setType(String type) { // 成员方法,设置其图形类型
this.type = type;
}
public String getType() {
return type;
}
}
//例题4_1求图形的面积和周长
public class TestCircle {
public static void main(String[] args) {
Circle myShape = new Circle();
myShape.setType("圆"); // 调用超类的方法
myShape.setRadius(5.2); // 调用子类的方法
System.out.println("myShape的类别是:" + myShape.getType());
double area = myShape.getArea();
System.out.println("myShape的面积是:" + area);
System.out.println("myShape的周长是:" + myShape.getPerimeter());
}
}
十一、继承的实现
语句格式: [修饰符] class 子类名 extends 父类名 { //定义新的属性 //重新定义父类中已有的属性 //定义新的成员方法 //重写父类中成员方法 }
补充说明: 类java.long.Object是一切类的父类或根类,为万类之源。 类继承,Java只支持继承,可通过使用接口机制来实现多重继承。
//父类_员工类
public class Employee {
//成员变量
int employeeID; //职工号
String name; //职工姓名
String address; //住址
double pay; //工资
//成员方法
public Employee() {
super();
}
public double getPay() {
//计算职工工资
return this.pay;
}
// get和set方法
public int getEmployeeID() {
return employeeID;
}
public void setEmployeeID(int employeeID) {
this.employeeID = employeeID;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public void setPay(double pay) {
this.pay = pay;
}
}
//例题4_2设计并实现公司的职工类
//子类_管理者类
class Manager extends Employee{
//新增自己的成员变量
String response; //职责
String department; //所在部门
//定义自己的成员方法
public void upPay(double p){
pay=pay+p; //这里的pay是Emplyee的变量
}
//由于管理者和普通职工计算工资方法不同,根据需要重新定义getPay()方法
public double getPay() { //计算管理者工资
return this.pay*1.5;
}
//测试
public static void main(String[] args) {
Manager manager=new Manager();
manager.upPay(100.0);
System.out.println(manager.getPay());
}
}
十二、 继承的规则
声明了直接子类继承直接父类,除构造方法外,子类可继承父类所有的变量和方法。 是否能访问,还要看其访问修饰符的控制范围。
十三、属性的继承
1. 属性的继承和扩展
子类可以继承父类所有的属性,还可以增加自己的成员变量。
2. 属性隐藏
属性隐藏是指子类重新定义一个从父类那里继承来的与变量完全相同的变量。 所谓隐藏,是指子类拥有了两个相同名字的成员变量,一个继承自父类,另一个是自己定义的成员变量。
属性隐藏时: 当子类执行继承父类的方法,处理的是继承自父类的变量。 当子类执行自己定义的方法,处理的是子类自己定义的变量。 在2种情况时,仍希望调用父类的属性,则需要用到super。
class Person {
String id;
String name="1";
String address="1";
public Person() {
}
public void showName() {
System.out.println(name+","+address); //输出的是父类中name
}
}
//例题4_3变量隐藏测试
class Student extends Person {
String address="2"; //重新定义父类变量
String school="2";
public Student() {
//无参构造
}
public void showInfo(){
showName(); //调用父类的方法
System.out.println("Student:"+address+"Student:"+school); //使用的是子类的address
}
//测试
public static void main(String[] args) {
Student student=new Student();
student.showInfo();
}
}
十四、方法的继承
1. 方法的继承和扩展
子类可以继承父类所有的方法,除构造方法外,还可以增加自己的成员方法。
2. 方法重写
方法重写(或覆盖是指子类重新定义从父类继承来的方法,从而使子类具有自己的行为。
注意事项: 1 同方法名、参数列表、范围值类型。同修饰符static,final方法不能重写。 2 访问限制,子类比父类扩大宽松原则,严格顺序:private>default>protected>public 3 部分重写父类方法,在原方法基础上添加新的功能,即在子类的覆盖方法的第一句位置加:super.父类方法名()
什么情况用? 子类实现与父类相同的功能,算法不同。 名字相同方法中,子类比父类操作多。 在子类中取消从父类继承的方法,将方法体设为空。
//子类-Ellipse椭圆类
class Ellipse extends Shape {
private int centerX; // 圆心X坐标
private int centerY; // 圆心Y坐标
private int width; // 椭圆宽度
private int height; // 椭圆高度
public Ellipse(int x, int y, int w, int h) {// 构造方法
super(); // 调用父类的构造方法1
centerX = x;
centerY = y;
width = w;
height = h;
}
public void draw() {// 覆盖父类的draw()方法
System.out.println("draw a Ellipse");
}
}
//子类-Rectangle矩形类
class Rectangle extends Shape {
private int left; // 矩形左上角X坐标
private int top; // 矩形左上角Y坐标
private int width; // 矩形长度
private int height; // 矩形宽度
public Rectangle(int l, int t, int w, int h) {// 构造方法
super(2); // 调用父类的构造方法2
left = l;
top = t;
width = w;
height = h;
}
public void draw() {// 覆盖父类的draw()方法
System.out.println("draw a Rectangle");
}
}
//父类-Shape形状类
class Shape {
protected int lineSize; // 线宽
public Shape() {// 构造方法1
lineSize = 1;
}
public Shape(int ls) {// 构造方法2
lineSize = ls;
}
public void setLineSize(int ls) { // 设置线宽
lineSize = ls;
}
public int getLineSize() {// 获得线宽
return lineSize;
}
public void draw() { // 画图
System.out.println("Draw a Shape");
}
}
//例题4_4设计一个画图程序
public class Test {
public static void main(String args[]) {
Ellipse ellipse = new Ellipse(30, 30, 50, 60); // 创建子类Ellipse的对象
//ellipse.setLineSize(2); // 调用父类方法重新设置lineSize 值为2
System.out.println("LineSize of ellipse : " + ellipse.getLineSize());
Rectangle rectangle = new Rectangle(0, 0, 20, 30); // 创建子类rectangle对象
//rectangle.setLineSize(3); // 调用父类方法重新设置lineSize属性为3
System.out.println("LineSize of rectangle : " + rectangle.getLineSize());
ellipse.draw(); // 访问子类方法
rectangle.draw(); // 访问子类方法
}
}
十五、关键字super
定义:使用super可以引用被子类隐藏的父类的成员变量或方法。 语法格式: super.成员变量名 super.方法名(实参列表)
1. 在子类的构造方法内部引用父类的构造方法。
在构造子类对象时,必须调用父类的构造方法。一般自动调用父类中默认的无参构造方法,如果没有默认的,则需要手动调用-super关键字。
super调用构造方法的代码在子类的构造方法中最多出现一句。不能和this调用构造方法的代码一起使用。当调用父类空构造方法时,super可以省略。
//例题4_5super引用父类构造方法
//父类SuperDemo1
public class SuperDemo1 {
public SuperDemo1() {
} //父类构造方法1
public SuperDemo1(int a) {
} //父类构造方法2
}
//例题4_5super引用父类构造方法
//子类SubDemo1
public class SubDemo1 extends SuperDemo1 {
public SubDemo1() {
super(); //可省略,调用父类构造方法1
}
public SubDemo1(int a) {
super(a); //必须放在第一句,调用父类构造方法2
}
public SubDemo1(String s) {
super(); //可省略,调用父类构造方法1
}
}
2. 在子类中调用父类中的成员方法。
子类调用继承了父类的成员方法,一般直接通过方法名调用。如果在子类覆盖了父类的成员方法后,则需要用super关键字调用。
//例题4_6super引用父类成员方法
//父类SuperDemo2
public class SuperDemo2 {
public void test() {
}
public void print(int a) {
System.out.println("SuperDemo2: " + a);
}
}
//例题4_6super引用父类成员方法
//子类SubDemo2
public class SubDemo2 extends SuperDemo2 {
public void print(int a) {
super.print(a); //super关键字表示调用父类方法
System.out.println("Sub Dem2");
}
public void t() {
super.test(); //super可省略
super.print(0); //不可省略,因为子类方法重写父类方法print()
}
}
3. 在子类中调用父类中的成员变量。
一般成员变量的覆盖是没有意义的,所以调用成员变量时,super可以省略。
十六、构造方法的继承
构造方法可以重载,不可以重写。 子类无条件继承父类的无参构造方法。 子类不能继承父类的有参构造方法,只能通过super调用。 子类中调用父类的构造方法,super必须放在第一句。
//例题4_7构造方法的继承与调用顺序示例
class Grandpa {
protected Grandpa() {
System.out.println("default Grandpa");
}
public Grandpa(String name) {
System.out.println(name);
}
}
//例题4_7构造方法的继承与调用顺序示例
class Father extends Grandpa {
protected Father() {
System.out.println("default Father");
}
public Father(String grandpaName, String fatherName) {
super(grandpaName); //调用父类的构造方法 有参
System.out.println(fatherName);
}
}
//例题4_7构造方法的继承与调用顺序示例
public class Son extends Father {
//执行顺讯:从最顶层父类,开始往下执行。
public Son() {
System.out.println("default Son");
}
public Son(String grandpaName, String fatherName, String sonName) {
super(grandpaName, fatherName); //调用父类有参构造
System.out.println(sonName);
}
public static void main(String args[]) {
//先执行有参
Son s1 = new Son("My Grandpa", "My Father", "My Son"); // ①创建子类对象1
//再执行无参
Son s2 = new Son(); // ②创建子类对象2
}
}
十七、对象类型转换
1. 向上转型
//实现向上转型,子类——>父类 Person p=new Student(); //父类Person引用子类Student。 p.talk();
补充说明: 丢失一些子类新增的变量和方法,但留下了子类继承或重写的变量和方法。 自动转换。 向上转型对象访问重写父类的方法,操作子类的方法。
2.向下转型
//实现向下转型,父类——>子类 Student s=(Student) p; //父类对象p赋给子类对象s,子类Student引用父类Person。 s.learn();
补充说明: 可以操作父类及子类的变量和方法。 强制转换。 向下转型对象访问重写父类的方法,操作子类的方法。
//此向下转型错误 报java.lang.ClassCastException 强制转换无法实现。 Person person=new Person(); Student st=(Student)person; 因为当前Student子类没引用person父类。 st.learn();
//例题4_8测试对象转型,定义父类Person、子类Student和Teacher
public class Person {
String name;
public void talk() {
}// 把父类中的覆盖情况去掉,则子类中的方法不再可见
public void listen() {
System.out.println("a person is listening..");
}
}
//例题4_8测试对象转型,定义父类Person、子类Student和Teacher
public class Teacher extends Person {
String workNo; // 工号
public void talk() {
System.out.println("teacher is talking");
}
public void teach() {
System.out.println("teacher is teaching..");
}
}
//例题4_8测试对象转型,定义父类Person、子类Student和Teacher
public class Student extends Person {
String no; // 学号
public void talk() {
System.out.println("student is talking");
}
public void learn() {
System.out.println("student is learning..");
}
public static void main(String[] args) {
//实现向上转型,子类——>父类 丢失一些子类新增的变量和方法,但留下了子类继承或重写的变量和方法。自动转换。
Person p=new Student(); //父类Person对象p引用子类Student新创建的对象。
p.talk();
//p.learn(); //丢失了
//实现向下转型,父类——>子类 可以操作父类及子类的变量和方法。强制转换。
Student s=(Student) p; //父类对象p赋给子类对象s,当前p是子类Student的引用。
s.learn();
//次向下转型错误 报java.lang.ClassCastException 强制转换无法实现。
Person person=new Person(); //这里没有引用Student对象
Student st=(Student)person;
st.learn();
}
}
3. instanceof运算符
作用:判断该引用变量是否属于该类或该类的子类。 格式:引用变量名 instanceof 类名
结果:是,返回True。不是,返回False
//例题4_9instanceof运算符的使用示例
public class UseOfInstanceof {
public static void main(String[] args) {
Person p1 = new Teacher(); // p1是父类对象,但是子类实例
Person p2 = new Student(); // p2是父类对象,但是子类实例
pleasetalk(p1); //调用子类Teacher中保留的talk
pleasetalk(p2); //调用子类Student中保留的talk
p1.listen(); //调用父类listen
p2.listen();
//p1.teach(); //p1的teach方法是子类方法。不可见
//p2.learn(); //p2的learn方法是子类方法,不可见
if (p1 instanceof Teacher) {
System.out.println("she is a teacher!");
((Teacher) p1).teach(); // 如果能够转换则让老师演示教学
}
if (p2 instanceof Student) {
System.out.println("he is a Student!");
((Student) p2).learn(); // 如果能够转换,则让学生演示学习
}
// p1.learn();//子类方法不可见
}
static void pleasetalk(Person p) {
p.talk();
}
}
十八、多态性
方法的多态,即多种形态,是指属性或方法在子类中表现为多种形态。
1. 编译时多态
编译多态是指在程序编译过程中出现的多态性,通过方法重载实现(静态多态)。
//通过方法重载实现静态多态。
//条件:只用方法重载
public class StopClass {
public static void print(){
System.out.println("print_1");
}
public static void print(int i){
System.out.println("print_"+i);
}
public static void print(char c){
System.out.println("print_"+c);
}
public static void main(String[] args) {
//在类(静态)方法中,可以通过类名调用静态方法,不能访问非静态成员。
print(); //这里类名可以省略
print(2);
print('3');
}
}
2. 运行时多态
运行时多态是指在程序运行时出现的多态性,通过方法重写实现(动态多态)。 动态多态存在的条件有三个:
- 类和类之间有继承关系。
- 有方法重写。
- 存在父类引用子类对象。
通过方法重载实现静态多态。 条件:只用方法重载
//定义父类Shape
class Shape {
public void draw(){ //父类的draw()方法
System.out.println("draw a Shape");
}
}
//定义子类Cirle
class Circle extends Shape {
public void draw(){ //重写父类的draw()方法
System.out.println("draw a Circle");
}
}
//定义子类Elipse
class Ellipse extends Circle { //定义子类Ellipse
public void draw(){ //重写父类的draw()方法
System.out.println("draw a Ellipse");
}
}
//例题4_10方法多态性示例
//动态多态条件:继承、重写、父类引用子列对象(对象类型转换——向上转型)
public class Test {
public static void main(String args[]) {
Shape s = new Shape(); //动态绑定为类Shape对象
Shape c = new Circle(); //动态绑定为类Circle对象
Shape e = new Ellipse(); // 动态绑定为类Ellipse对象
s.draw(); //Shape对象访问父类Shape的方法
c.draw(); //Shape对象访问子类Circle的方法
e.draw(); //Shape对象访问子类Ellipse的方法
}
}
***3. 重载和重写的区别
重载和重写时面向对象设计的两大特征。有相似之处,也有区别:
- 方法重载是同一类中的方法之间的关系,是水平关系。方法重写是子类和父类之间,是垂直关系。
- 方法重载是多个同名不同参的方法。方法重写是对同一个方法产生关系,要求同名/同参数/同返回类型。
- 方法重载对控制修饰符没有要求。方法重写要求不能降低父类的访问控制权限。
十四、抽象类和最终类
1. 抽象类
定义:类的声明中有abstract关键字的类称为抽象类。 作用:为子类定义共同特征和某些依赖于具体实例而实现的抽象方法。用于对某些类进行概括和抽象,即抽象类定义其子类共有的属性和方法,以免子类重复定义。
声明格式: public abstract class 抽象类名{ 类体; }
抽象类特点: 不能用new创建抽象类实例 与具体类不同的是,抽象类中可以定义抽象方法。 抽象方法只能出现在抽象类中,抽象类中可以没有抽象方法。 抽象方法必须要在非抽象子类中实现,否则子类也必须声明为抽象类。
2. 抽象方法
定义:类的成员方法有abstract关键字修饰的方法称为抽象方法。 作用:描述系统的功能或规范某些操作,不提供集体的实现。抽象方法的实现通常由继承该类的子类去完成。
语句格式: 权限修饰符 abstract 返回值类型 方法名(形参参数列表);
使用抽象方法: abstract于static不能同时存在。 任何包含抽象方法的类必须被声明为抽象类。 在程序设计中,抽象类一定是某个类或某些类的父类。 若干个抽象类的子类要实现一些同名的方法。
abstract class Animal {
String str;
Animal(String s) { //定义抽象类的一般方法-构造方法
str = s;
System.out.println("animal:"+str);
}
abstract void eat(); //定义抽象方法
}
class Dog extends Animal {
String str;
Dog(String s) {
//调用父类的有参构造
super(s);
}
//继承了抽象类,就必须实现抽象类的子类
void eat() {
System.out.println("狗吃骨头!");
}
}
class Horse extends Animal {
String str;
Horse(String s) {
//调用父类的有参构造
super(s);
}
//继承了抽象类,就必须实现抽象类的子类
void eat(){ // 重写父类的抽象方法
System.out.println("马吃草料!");
}
}
//例题4_11抽象类和抽象方法示例
public class Test {
public static void main(String args[]) {
Animal h = new Horse("马");
Animal d = new Dog("狗");
h.eat();
d.eat();
}
}
3. 最终类和最终方法
最终类定义:最终类是指不能被继承的类,即不能再用最终类派生子类。Java规定,最终类中方法都自动成final方法。
最终方法定义:最终方法是指不能被子类重写修改的方法。
语句格式: public final class 类名{ //最终方法 public final 返回类型 方法名(){ } }
使用注意: 一个类不能既是最终类,又是抽象类,即abstract和final不能合用。 权限修饰符public等,一般放在abstract和final的前面。
class Person {
String name;
public Person() {
} // 构造方法
final String getName() // 最终方法
{
return "person";
}
}
//例题4_12最终方法示例
class Student extends Person {
//构造方法
public Student() {
}
//final String getName() //重写父类的最终方法,不允许
//{
// return "student";
//}
}
十五、接口
1. 接口的定义
一个类通常规定了同一类事物应用的静态属性和动态行为,而一个接口规定了一系列类应用的共同属性和行为。 一个只能继承一个父类,但允许一个类实现(可以说继承)多个接口。抽象类和接口功能类同,实现Java语言的多继承机制是通过接口实现的。
主要功能:
- 实现不相关类的相同方法。
- 指明多个类需要实现的方法。
- 在运行时动态定位类所需调用的方法。
定义格式:
修饰符[public] [abstract] interface 接口名 [extends 父接口列表] //接口声明 { //接口体是有属性(常量)和方法方法声明(抽象方法声明)组成 [public][static][final] 类型 常量名=值; //常量声明 [public][abstract] 返回类型 方法名(参数列表); //抽象方法的声明 }
注意事项: 接口中的属性默认是public static final修饰的常量,必须赋初值。 接口中的方法默认是public abstract修饰的,没有方法体。 接口文件名必须与接口名一致。
2. 接口的实现
定义类时实现一个借口用关键字implements。 语句格式: [修饰符] class 类名 implements <借口名1>,<借口名2>, ...
注意事项:
- 如果某个类实现了借口,这个类就可以访问借口中的常量。
- 类实现借口方法时,访问控制符必须是public,因为借口方法都是public类型。
- 实现接口的类,如果不是抽象类,则必须为所有抽象方法定义方法体,方法头部与接口中的定义一致。
//例题4_13编写接口PrintMessage用来处理各种打印信息操作
public interface PrintMessage {//接口声明
public int count = 10; //常量声明和赋值
public void printAllMessage(); //抽象方法声明
public void printLastMessage(); //抽象方法声明
public void printFirstMessage(); //抽象方法声明
}
//例题4_14编写一个实现接口PrintMessage的类,并编写一个测试程序进行测试
public class MyPrint implements PrintMessage{ //实现接口的类MyPrint
private String[] message ; //类中的成员变量message 字符串数组
private int i; //类中的成员变量i
public MyPrint(){ // MyPrint类的构造方法
message = new String[3];
i = 0;
this.putMessage("Hello world!"); //使用MyPrint类的方法
this.putMessage("Hello China!");
this.putMessage("Hello JIANGZHOU!");
}
public void putMessage(String str) { //类中的方法
message[i++] = str;
}
public void printAllMessage(){ //实现接口中的方法
for (int k = 0; k < message.length; k++) {
System.out.println(message[k]);
}
}
public void printLastMessage(){ //实现接口中的方法
System.out.println(message[message.length - 1]);
}
public void printFirstMessage(){ //实现接口中的方法
System.out.println(message[0]);
}
}
//例题4_14编写一个实现接口PrintMessage的类,并编写一个测试程序进行测试
public class Test {
public static void main(String[] args) {
MyPrint my = new MyPrint(); //定义MyPrint类的对象
System.out.println("print all messages");
my.printAllMessage(); //使用实现了的接口方法
System.out.println("print the first messages");
my.printFirstMessage(); //使用实现了的接口方法
System.out.println("print the last messages");
my.printLastMessage(); //使用实现了的接口方法
}
}
3. 抽象类与接口的区别
abstract class 和 interface是Java对抽象类定义支持的两种机制。 语法上:抽象类用关键字abstract class定义,并可以定义自己的成员变量和非抽象的成员方法。接口用interface定义,接口内只有静态的常量,所有的成员方法都是抽象方法。 使用上:抽象类的使用,表示的是一种继承关系,一个类只能使用一次继承关系。但是一个类可以实现多个接口。 设计上:abstract class表示的是"is-a"关系。interface表示的是"like-a"关系。
十六、包
1. 包的概念
主要定义:包是Java提供的文件组织方式。一个包等于一个文件夹,一个包可以包括很多类文件。包中还有子包,形成包等级。 一个类文件放在包中,就会有两个名字。一个是类文件的短名字(类文件本身),一个是类文件的全限定名(在类文件本身前加上包名)。
主要作用:方便管理,方法软件资源重用,扩大了Java命名空间。
2. 创建包
语法格式:package 包名; 多级包:package 包名 [.子包名 [.子包名 ...]];
3. import语句
通用格式: import package1 [.package2].(类名|*);
注意事项: 除非是文件系统的限制,不存在对于包层次深度的实际限制。 要么指定一个明确的类名,要么使用*号导入所有public类,但后者会增加编译时间,对运行时间性能无影响。
4. 访问保护
对于类成员而言:声明为public的内容可以被任何地方访问。声明为private的成员不能被该类外看到。 没有明确访问说明,则为默认访问控制符,它仅可以被同一包中其他类访问。