零、复习昨日
继承好处:减少冗余、提高复用、多态的前提
继承后:子类可以使用父类非私有属性和方法,父类不能向下使用子类任何的成员
super:代表父类的引用,可以使用父类方法属性和方法
重写:父类的方法不适用于子类,子类需要重写改变以适用自己
重写的要求:访问修饰符、返回值类型、方法名、参数列表全部一样
特殊的:访问修饰的权限 子类 >= 父类
对象创建的过程:
只有一个类:1.开辟空间;2.属性初始化;3.执行构造方法内的代码;4.地址值赋值给对象。
子类创建对象:先执行父类的属性初始化,再执行父类构造方法内的代码;再对子类对象初始化,子类构造方法内代码执行
一、final
final:最终;可以修饰类、属性、方法;
final修饰类,类不能被继承(太监类...)
final修饰属性及变量,变量不再变化,为常量;常量不能被重新赋值;常量命名全部大写(A , ID_CARD……)
final修饰方法,方法不能被重写
public class TestFinal {
// a成为常量,不能再变化
// 一般来说,常量命名全部大写
final int A = 1;
final String ID_CARD = "11111111";
int c = 1;
public void test(){
// A = 2; // 常量不能被重写赋值
c = 2;
}
}
// 类不能继承
/*final*/ class Super{
// 方法不能重写
final void m1(){}
}
class Sub extends Super{
// @Override
// void m1() {
//
// }
}
二、static
2.1 static语法特点
static:静态的;
静态修饰属性和方法
一旦修饰属性和方法,那么属性和方法就会变成==类属性、类方法==
被static修饰的属性和方法随着类加载而初始化
且被static修饰的属性和方法在内存中只有一份,被所有该类共享
静态修饰的属性和方法==不会随着创建对象进堆中==,就存在于静态域中
以前每次创建对象,每个对象都有属于自己的属性,这些属性叫对象属性。现在static修饰了属性和方法后,这属性和方法不再属于某个对象,而是属于类,称为类属性类方法
public class Student {
private String name;
private int age;
static String school;
// set get...
// 打印学生信息
public void printInfo(){
System.out.println("姓名:"+name+",年龄:"+age+",学校:"+school );
}
}
public class TestStatic {
public static void main(String[] args) {
Student s1 = new Student("亚瑟",18,"德玛西亚学院");
s1.printInfo();
Student s2 = new Student("安其拉",9);
s2.printInfo();
Student s3 = new Student("李白",29);
s3.printInfo();
// 发现s2,s3虽然没有赋值school,但是输出有值
}
}
2.2 语法应用
静态修饰的属性和方法建议直接通过类名调用;
因为:静态的属性和方法随着类加载就已经出现在内存中,并初始化成功,意味着可以使用了,就没有必要再通过创建对象后使用对象去调用,这样反而耽误时间和空间!!
练习时将测试类定义成static,方便使用
static方法只能调用静态方法和静态属性
因为:静态修饰的属性和方法随着类加载就已经出现在内存中,并初始化成功,而对象属性对象方法(非静态属性和非静态方法)是需要创建对象才会出现。即:有静态属性和静态方法时,那些没静态的属性和方法还没出现
总结:静态只能调用静态
非静态方法能调用静态方法和静态属性
静态方法内不能使用this
public class TestStatic {
public static void main(String[] args) {
// 类名调用静态属性
Student.school = "啦啦啦啦";
// 类名调用静态方法
Student.study();
Student s1 = new Student("亚瑟",18);
// 不建议通过对象调用静态属性和方法
s1.school = "zzzz";
s1.study();
s1.printInfo();
Student s2 = new Student("安其拉",9);
s2.printInfo();
Student s3 = new Student("李白",29);
s3.printInfo();
}
public static void m1(){
// 静态内不能使用this
//System.out.println(this );
// 静态不能调用非静态
//m2();
}
public void m2(){
System.out.println(this );
// 非静态可以调用静态
m1();
}
}
2.3 总结
什么时候需要使用static?
main方法方便调用使用其他方法时,将其他方法定义成静态
但有些数据需要被共享时,就需要使用static.例如:火车票剩余票数:需要被所有售票窗口共享
三、多态
多态
生活中的多态:蝌蚪
程序中的多态:某个方法的多个形态,同一个方法执行时会出现不同的效果
==多态形成的前提条件==
有继承关系(或者实现接口)
子类重写父类方法
父类引用指向子类对象,也叫向上转型
==编译看父类,运行看子类==父类引用只能调用父类内有的方法,不能调用子类的方法
package com.qf.poly;
/**
* --- 天道酬勤 ---
*
* @author QiuShiju
* @desc
*/
public class TestPoly {
public static void main(String[] args) {
// 父类引用指向子类对象(向上转型)
Animal animal = new Dog();
/**
* 编译看父类,运行看子类
* 父类引用只能调用父类内有的方法,不能调用子类的方法
*/
//animal.eat();
Animal animal2 = new Cat();
//animal2.eat();
// 饲养员开始喂
// Dog dog = new Dog( );
// feedDog(dog);
// feedCat(new Cat( ));
feedAnimal(new Dog());
feedAnimal(new Cat());
feedAnimal(new Tiger());
}
/**
* 饲养员喂动物,让它们吃
* 但是动物园每新来一个动物都要重新创建一个方法,很麻烦
*/
public static void feedDog(Dog dog) {
dog.eat();
}
public static void feedCat(Cat cat) {
cat.eat();
}
/**
* 使用多态将方法改造,以达到扩展性好
*/
public static void feedAnimal(Animal animal) {
animal.eat();
}
}
四、向上转型&向下转型
向上转型:父类引用指向子类对象,子类对象向上转型成了父类
向下转型:父类引用向下转型成为子类默认不允许,可以通过强制类型转换来完成 Dog dog = (Dog) animal;
什么时候需要向下转型?但需要调用子类特有的方法时,就需要将父类引用向下转型成子类对象,才可以使用子类特有的方法!
/**
* 使用多态将方法改造,以达到扩展性好
*/
public static void feedAnimal(Animal animal) {
animal.eat();
// 判断animal内部是什么类型的对象
if (animal instanceof Dog) {
Dog dog = (Dog) animal;
dog.watchHome();
} else if (animal instanceof Cat) {
Cat cat = (Cat) animal;
cat.catchMouse();
}
}
五、多态应用
package com.qf0215;/**
* @escription:
* @author:灰总
* @data:
*
*/
class Shape {
public double area(){
return 0.0;
}
public double girth(){
return 0.0;
}
}
class Circle extends Shape {
private double r;
public Circle(){}
public Circle(double r) {
this.r = r;
}
public void setR(double r){
this.r = r;
}
public double getR(){
return r;
}
@Override
public double area() {
return 3.14*r*r;
}
@Override
public double girth() {
return 2*3.14*r;
}
}
class Rect extends Shape {
private double length;
private double width ;
public Rect(){}
public Rect(double length, double width) {
this.length = length;
this.width = width;
}
public void setLength(double length) {
this.length = length;
}
public double getLength() {
return length;
}
public void setWidth(double width) {
this.width = width;
}
public double getWidth() {
return width;
}
@Override
public double area() {
return length * width;
}
@Override
public double girth() {
return 2*(length + width);
}
}
class Square extends Rect {
private double l;
public Square(){}
public Square(double l) {
this.l = l;
}
public double getL() {
return l;
}
public void setL(double l) {
this.l = l;
}
@Override
public double area() {
return l*l;
}
@Override
public double girth() {
return 4*l;
}
}
public class TestShape{
public static void main(String[] args) {
// 创建一个形状数组
Shape[] shapes = new Shape[3];
// 相当于Shaper shape = new Circle();
// 这就是向上转型
shapes[0] = new Circle(2);
shapes[1] = new Rect(5,4);
shapes[2] = new Square(4);
for (int i = 0; i < 3; i++) {
// 从数组取出的是父类引用,相对于是Shape shape,
// 但是运行的时候看子类的效果
System.out.println(shapes[i].area());
System.out.println(shapes[i].girth());
}
}
}
设计一个合理的程序:有一个灯泡(Bulb)类.灯泡有发光的方法,他有两个子类,分别是红灯泡和绿灯泡;
还有一个台灯(TableLamp)类,他有一个开灯的方法,实现开红灯,红灯亮,发红光,开绿灯,绿灯亮,发绿光;
public class Bulb {
public void light() {
System.out.println("灯泡发光" );
}
}
class GreenBulb extends Bulb {
@Override
public void light() {
System.out.println("绿灯泡,发绿光" );
}
}
class RedBulb extends Bulb{
@Override
public void light() {
System.out.println("红灯泡,发红光" );
}
}
class TableLamp {
public void open(Bulb bulb) {
bulb.light();
}
public static void main(String[] args) {
// 买台灯
TableLamp tableLamp = new TableLamp( );
// 买不同颜色的灯泡
RedBulb redBulb = new RedBulb( );
GreenBulb greenBulb = new GreenBulb( );
PinkBulb pinkBulb = new PinkBulb( );
// 装灯,开灯
tableLamp.open(redBulb);
tableLamp.open(greenBulb);
tableLamp.open(pinkBulb);
}
}
总结:多态到底怎么用?什么时候用?
大部分的多态使用场景就是将 方法的参数列表设计成父类类型,调用方法时传入子类类型,运行得到子类的效果