一、继承
- 继承中的成员方法的访问:
如果子类和父类的成员方法名称不一致,分别访问即可!
子类和父类的成员方法名称一致,
子类中存在,访问子类的;
如果不存在,访问父类,如果父类没有,报错!
举例:
//定义一个父类
class Fu{
public void method(){
System.out.println("method fu...");
}
}
//子类
class Zi extends Fu{
public void show(){
System.out.println("show zi...");
}
public void method(){
System.out.println("method zi...");
}
}
//测试类
public class ExtendsDemo {
public static void main(String[] args) {
//创建子类对象
Zi z = new Zi() ;
z.show();
z.method();
}
}
- 成员方法的分类:
是否存在返回值,是否带参
根据具体需求然后定义相对应的功能!
参数类型:
基本类型/引用类型
返回值类型:
基本类型/引用类型
举例:
//定义一个父类
class Father{
//带参的,没有返回值类型
public void show(String str){
System.out.println(str);
}
//带参的,有返回值类型的
public String method(int num){
return "helloworld"+num ;
}
}
//子类
class Son extends Father{
//子类的成员方法
//不带参的,没有返回值的
public void function(){
System.out.println("function son...");
}
//不带参的,有返回值的
public String function2(){
return "JavaEE" ;
}
}
//测试类
public class ExtendsDemo2 {
public static void main(String[] args) {
//创建子类对象
Son s = new Son() ;
s.show("helloworld,javaee");
String str = s.method(100);
System.out.println(str);
s.function();
String str2 = s.function2();
System.out.println(str2);
}
}
-
final关键字修饰基本类型/引用类型的区别
final:本身:最终的,无法更改的 状态修饰符当final修饰基本数据类型,基本数据类型的值不能再改变,只能赋值一次!
开发中,定义基本数据类型的常量:
在一个类的成员位置:
public static final int xxx = xx ;当final修饰引用数据类型,引用数据类型的地址值不能在改变!
在开发中, 在一个类中引用类一个类的实例!
举例:
class Demo{
//传统定义方式
public static final int s1 = 100 ;
public static final int s2 = 200 ;
public static final int s3 = 300 ;
public static final int s4 = 400 ;
public static final String str1 = "FRONT" ;
public static final String str2 = "BEHIND" ;
public static final String str3 = "LEFT" ;
public static final String str4 = "RIGHT" ;
//常量:引用类型
public static final Student s = new Student() ; //静态实例变量
}
enum Demo2{ //枚举类型(JDK5以后的枚举)
//一堆字符串常量
FRONT,BEHIND,LEFT,RIGHT;
}
//定义学生类
class Student{
int age = 20 ; //成员位置
}
//测试类
public class FinalDemo {
public static void main(String[] args) {
//定义一个变量:
int num = 100 ;
System.out.println(num);//这是一个变量
System.out.println("--------------------");
//定义final修饰的变量
final int num2 ;//final修饰的基本数据类型的变量
num2 = 200 ; //只能赋值一次,常驻内存(已经是常量:自定义常量)
System.out.println(num2);
// num2 = 300 ;
System.out.println("---------------------");
int x = 100 ;
x = 1000 ;
System.out.println(Demo.s1) ;
System.out.println(Demo.s2) ;
System.out.println(Demo.s3) ;
System.out.println(Demo.s4) ;
System.out.println("------------------------");
//创建一个学生类对象;
//改变当前类中的成员变量:依然可以更改,但是此时不能在重新new对象了
final Student s = new Student() ;//com.qf.extends_01.Student@1540e19d
System.out.println(s);
s.age = 30 ;
System.out.println(s.age);
s.age = 25 ;
System.out.println(s.age);
// s = new Student() ; 不能重新进行实例化了(final修饰之后,学生对象的地址值无法改变的!)
}
}
二、多态:一个事物在不同时刻体现的不同形态(内存中的变化)
举例:
水是一个事物
水,固态,也可以是气态,也是液体!
猫是一个事物
猫也可以叫做动物
- 要使用多态,要遵循它的前提条件:
1)必须存在继承关系 (没有继承关系,不谈多态!)
2)必须有方法重写 (子类继承父类,需要将父类的功能覆盖掉),使用子类的功能;
3)必须存在父类引用指向子类对象(“向上转型”)
class Fu{}
class Zi extends Fu{}
之前:Zi zi = new Zi() ;
现在:多态: Fu f = new Zi() ; - 在多态中,的成员访问特点:
Fu f = new Zi() ;
非静态
成员变量:编译看左,运行看左
成员方法:编译看左,运行看右 :由于存在子类重写父类的功能,所以使用的子类的功能
静态方法: 静态的东西都是跟当前类有关系:优先进内存(随着类的加载而加载)
静态方法–算不上方法重写:编译看左,运行看左
构造方法:父类先初始化,然后子类在进行初始化
举例:
//父类
class Fu{
public Fu(){
System.out.println("这是父类的无参构造方法");
}
int num = 20 ;
public void show(){
System.out.println("show Fu");
}
//静态方法
public static void function(){
System.out.println("function Fu");
}
}
//子类
class Zi extends Fu{
public Zi(){
//super();
System.out.println("这是Zi类的无参构造方法");
}
int num = 30 ;
//重写
public void show(){
System.out.println("show Zi");
}
//静态方法
public static void function(){
System.out.println("function Zi");
}
}
//测试类
public class DuoTaiDemo {
public static void main(String[] args) {
//父类引用指向子类对象
Fu f = new Zi() ; //向上转型
System.out.println(f.num);
// System.out.println(f.num2); num2在Fu类中不存在
f.show() ;
f.function();
// Fu.function();
}
}
- 多态的好处是什么?
1)可以提高代码的复用性 (是由继承保证的)
2)可以提高代码的扩展性(多态完成:父类引用指向子类对象:Fu f = new Zi()) ;
举例:
//动物类
class Animal{
//吃
public void eat(){
System.out.println("动物都需要吃饭...");
}
//睡
public void sleep(){
System.out.println("动物困了都需要休息...");
}
}
//定义狗类
class Dog extends Animal{
public void eat(){
System.out.println("狗吃骨头...");
}
public void sleep(){
System.out.println("狗躺着睡觉...");
}
}
//定义猫类
class Cat extends Animal{
public void eat(){
System.out.println("猫吃鱼...");
}
public void sleep(){
System.out.println("猫趴着睡觉...");
}
}
//猪类
class Pig extends Animal{
public void eat(){
System.out.println("猪吃白菜...");
}
public void sleep(){
System.out.println("猪卧着睡觉...");
}
}
//优化2:定义一个动物工具类
class AnimalTool{
//构造方法私有化
private AnimalTool(){}
//定义功能:养狗
/*
public static void createDog(Dog d){ //形式参数 狗类型
d.eat();
d.sleep();
}
//养猫
public static void createCat(Cat c){
c.eat();
c.sleep();
}
//养猪
public static void createPig(Pig p){
p.eat();
p.sleep();
}
*/
public static void createAnimal(Animal a){ //实际参数传递:Animal a= new Cat() ;
//Animal a = new Dog ();
a.eat();
a.sleep();
}
}
//测试类
public class DuoTaiDemo2 {
public static void main(String[] args) {
//之前的写法
//喜欢狗,养了一只狗
Dog d = new Dog() ; //内存是狗 Animal a = new Dog() ;//内存是狗 :狗是动物
d.eat();
d.sleep();
Dog d2 = new Dog() ;
d2.eat();
d2.sleep();
System.out.println("----------------------");
//养一只猫
Cat c = new Cat() ;
c.eat();
c.sleep();
Cat c2 = new Cat() ;
c2.eat();
c2.sleep();
System.out.println("==========================================");
//优化1: 需要提供 名字createCat(Cat c) /createDog(Dog d)
//测试
createDog(d);
createDog(d2);
createCat(c);
createCat(c2);
System.out.println("=======------------------======================");
//上面的优化可以使用:但是不好:
//当前具体动物类越来越多的时候,createXXX(具体动物类型 参数名) 越来越多,代码量大!
//继续优化2:
//单独定义一个类: 针对动物操作的工具类:AnimalTool
/*
AnimalTool.createDog(d);
AnimalTool.createCat(c);
Pig pig = new Pig() ;
AnimalTool.createPig(pig);
*/
//上面虽然可以,但是很麻烦,动物类越来越多,功能也越来越多,没有丝毫扩展,代码量很大
//父类引用指向子类对象 :Animal a = new Cat() / new Dog() /new Pig() ;
//方法的参数参数如果是父类型,
AnimalTool.createAnimal(d);
AnimalTool.createAnimal(c);
AnimalTool.createAnimal(new Pig());
}
//定义养猫和养狗的功能
public static void createCat(Cat c){ //形式参数 猫类型 ---->实际传递---需要当前这个类的对象
c.eat();
c.sleep();
}
public static void createDog(Dog d){ //形式参数 狗类型
d.eat();
d.sleep();
}
}
-
多态的弊端:
不能访问子类的特有功能!向上转型:父类引用指向子类对象
如果能够使用子类型,就可以访问它的功能!
解决方案:
1)子类 引用 指向自己本身:子类对象
虽然可以访问,但是多态中,不推荐,因为重新new 对象,消耗堆内存空间
2) 将父类引用强制转换为子类引用 :向下转型 ,前提:必须存在向上转型
Fu f = new Zi() ;//向上转型
Zi z = (Zi)f ; //向下转型
推荐2) :不会重新在堆内存中开辟空间,结束内存空间!
举例:
class Father{
public void show(){
System.out.println("show Father...");
}
}
//子类
class Son extends Father{
public void show(){
System.out.println("show Zi...");
}
//子类的特有功能
public void playGame(){
System.out.println("可以玩游戏...");
}
}
//测试类
public class DuoTaiDemo3 {
public static void main(String[] args) {
//多态的创建对象
Father father = new Son() ; //向上转型:父类引用指向子类对象
father.show() ;
// father.playGame() ;
Son s = new Son() ;
s.playGame();
Son s2 = (Son) father; //向下转型
s2.playGame();
}
}
-
多态的向下转型,如果使用不当,会出现异常!
ClassCastException:类转换异常:
堆内存中的变化 和接收的类型不匹配!
举例:
//父类
class Animal2{
}
//子类
class Cat2 extends Animal2{
}
class Dog2 extends Animal2{}
//测试类
public class DuoTaiDemo4 {
public static void main(String[] args) {
//多态创建对象
Animal2 a = new Cat2() ; //堆内存中猫的实例 (猫是动物)
Cat2 c = (Cat2)a ; //将猫还原了 (猫是猫) 向下转型
a = new Dog2() ;//堆内存中是狗的实例(狗是动物)
Dog2 d = (Dog2)a; //还原成狗了
// Cat2 c2 = (Cat2)a; //ClassCastException:类转换异常:属于运行时期异常!
}
}
```java
综合练习:
/*需求:
设计程序:
有一个台灯:Lamp ,台灯有开灯的功能on,
它里面有一个灯泡属性bubble ,灯泡有发亮的功能,红灯泡,绿灯泡分别可以发红光,可以发绿光,
使用面向编程方式去设计这个程序
分析:
台灯--->是一个真实事物 --->定义台灯类
灯泡--->也是真实事物 --->定义灯泡类
*/
//台灯类
public class Lamp {
//灯泡属性
private Bubble bubble ;//灯泡属性
//开灯的功能
public void on(Bubble bubble){ //开灯,灯泡要发亮
bubble.shine();
}
}
//创建灯泡类
public class Bubble {
//发亮的功能
public void shine(){
System.out.println("灯泡可以发亮");
}
}
public class GreenBubble extends Bubble {
@Override
public void shine() {
System.out.println("灯泡可以发绿光了...");
}
}
public class RedBubble extends Bubble {
//发亮:发红光
public void shine(){
System.out.println("灯泡可以发红光...");
}
}
public class Test {
public static void main(String[] args) {
//创建台灯类对象
Lamp lamp = new Lamp() ;
//父类引用指向子类对象
Bubble bubble = new RedBubble() ;
lamp.on(bubble);
Bubble bubble2 = new GreenBubble() ;
lamp.on(bubble2);
System.out.println("-----------------");
lamp.on(new RedBubble()) ;
lamp.on(new GreenBubble()) ;
}
}
三、抽象类
- 什么是抽象类?
在现实世界存在真实事物,本身就是概括性的事物,这个时候,为了将具体的事物的功能给出具体体现,那么当前概括性的事物中
加入抽象-----abstract,并且它里面的一些功能仅仅只是声明,并不给出具体体现!
举例:
猫狗案例---->抽取动物类 :父类 (概括性的)
吃/睡的功能 ---- 只有具体的猫和狗,才有具体的吃的,睡的功能!
java提供了关键字----> 在类上加入一个修饰: abstract
abstract class 抽象类名{
}
如果一个类中有抽象方法,那么这个类一定是抽象类;
如果一个类是抽象类,不一定有抽象方法!
-
什么叫抽象方法?
抽象方法就是没有方法体的方法;public abstract 返回值类型 方法名(形式参数);
-
抽象类有一个特点:
抽象类不能实例化-----不能 new抽象类的子类:
1)是抽象类,也不能实例化!
如果仅仅存在父类,和子类,这个子类还是抽象类—毫无意义! new 不了!
2)研究的具体类: 可以实例化的!
对象的创建还是子类创建的!抽象的父类名 对象名 = new 子类名() ; //抽象类多态
抽象类的本质---->就是强制子类必须要将父类的中抽象功能,重写!
举例:
/*猫狗案例---继承版---->改造
Animal:抽象类 + 多态进行测试!
eat() ;
sleep();
分析:
从具体到抽象
猫: 姓名,年龄,颜色
行为:吃,睡,玩游戏
狗:姓名,年龄,颜色
行为:吃,睡,看门
将猫和狗中共有特性抽取到一个独立的事物:动物事物
Animal--->抽象类:abstract修饰
属性:姓名,年龄,颜色
行为:吃,睡 ----定义抽象方法 :public abstract ....
猫和狗都继承自Animal
猫:玩游戏:特有功能
狗:看门:特有功能
代码实现:
从抽象到具体
定义抽象的Animal
定义子类
测试类:多态
进行测试*/
//抽象的父类
public abstract class MyAnimal {
private String name ;//姓名
private int age ;//年龄
private String color ;//颜色
//无参构造方法
public MyAnimal() {
}
//有参构造
public MyAnimal(String name, int age, String color) {
this.name = name;
this.age = age;
this.color = color;
}
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;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
//吃:只有具体的动物:才能吃的是什么,eat方法:仅仅是一个声明,没有方法体
public abstract void eat() ; //抽象类要定义抽象方法的时候,abstract不能省略!
public abstract void sleep() ;
}
public class MyCat extends MyAnimal {
//有参/无参
public MyCat() {
//super() ;
}
public MyCat(String name, int age, String color) {
super(name, age, color);
}
@Override
public void eat() {
System.out.println("猫吃鱼");
}
@Override
public void sleep() {
System.out.println("猫趴着睡觉");
}
//特有功能
public void playGame(){
System.out.println("猫和老鼠玩游戏...");
}
}
public class MyDog extends MyAnimal {
public MyDog() {
}
public MyDog(String name, int age, String color) {
super(name, age, color);
}
@Override
public void eat() {
System.out.println("狗吃骨头");
}
@Override
public void sleep() {
System.out.println("狗躺着睡觉");
}
//特有功能
public String lookDoor(){
return "狗可以看门" ;
}
}
public class AbstractTest {
public static void main(String[] args) {
//多态进行测试
//创建一只狗
//无参构造+setXXX(xx)
MyAnimal ma = new MyDog() ;//向上转型
ma.setName("小黑");
ma.setAge(5);
ma.setColor("黑色");
System.out.println(ma.getName()+"---"+ma.getAge()+"---"+ma.getColor());
ma.eat();
ma.sleep();
// ma.lookDoor() ;//父类中不存在
MyDog mydog = (MyDog) ma ;
mydog.lookDoor() ;
System.out.println("------------------------------------------------");
//方式2:有参构造直接赋值
ma = new MyDog("哮天犬",100,"黑色") ;
System.out.println(ma.getName()+"---"+ma.getAge()+"---"+ma.getColor());
ma.eat();
ma.sleep();
MyDog mydog2 = (MyDog) ma ; //向下转型
mydog2.lookDoor() ;
}
}