https://www.cnblogs.com/best/p/6671434.html
目标
- 继承
- 封装
- 抽象类和接口
- 多态
java的三大特性
- 继承
定义:也称泛化,继承性是子类自动共享父类属性和方法的机制,是类与类之间的关系。java是单继承
-
封装
隐藏属性、方法或实现细节的过程称为封装。所谓封装是把对象的属性和行为结合在一个独立的系统单位内部 尽可能隐蔽对象的内部细节(private),只向外部提供接口(public) 降低对象间的耦合度
封装的重要意义:
使对象能够集中而完整地描述并对应一个具体事物
体现了事物的相对独立性,使对象外部不能随意存取对象的内部数据
- 多态
一个方法可以有多种实现,子类继承父类的方法后,可以进行重写
接口和抽象类(******)
继承
为什么需要继承:
package com.zhangguo.c42;
/**狗*/
public class Dog {
/**名称*/
public String name;
/**颜色*/
public String color;
/**价格*/
public double price;
/**显示信息*/
public void show(){
System.out.println("名称:"+name+",颜色:"+color);
}
}
/**猫*/
public class Cat {
/**名称*/
public String name;
/**颜色*/
public String color;
/**重量*/
public double weight;
/**显示信息*/
public void show(){
System.out.println("名称:"+name+",颜色:"+color);
}
}
/*动物园/
public class Zoo {
public static void main(String[] args) {
Dog dog=new Dog();
dog.name="吉娃娃狗";
dog.color="绿色";
dog.price=19800.7;
dog.show();
Cat cat=new Cat();
cat.name="波斯猫";
cat.color="红色";
cat.weight=18.5;
cat.show();
}
}
上面的代码实现了基本功能,但有问题,主要是:name,color,show重复,如果系统中的动物类再增加将不停的重复,重复就会带来不便修改,不便维护的问题。
用继承
/**动物*/
public class Animal {
/**名称*/
public String name;
/**颜色*/
public String color;
/**显示信息*/
public void show(){
System.out.println("名称:"+name+",颜色:"+color);
}
}
/**狗继承自动物,子类 is a 父类*/
public class Dog extends Animal {
/**价格*/
public double price;
}
/**猫*/
public class Cat extends Animal {
/**重量*/
public double weight;
}
/**动物园*/
public class Zoo {
public static void main(String[] args) {
Dog dog=new Dog();
dog.name="吉娃娃狗";
dog.color="绿色";
dog.price=19800.7;
dog.show();
Cat cat=new Cat();
cat.name="波斯猫";
cat.color="红色";
cat.weight=18.5;
cat.show();
}
从示例中可见dog并没有定义color属性,但在使用中可以调用,是因为dog继承了父类Animal,父类的非私有成员将被子类继承。如果再定义其它的动物类则无须再反复定义name,color与show方法。
课堂练习
练习写上面的程序
2 Java继承的特征
2.2.1、传递性
若类C继承类B,类B继承类A(多继承),则类C既有从类B那里继承下来的属性与方法,也有从类A那里继承下来的属性与方法,还可以有自己新定义的属性和方法。继承来的属性和方法尽管是隐式的,但仍是类C的属性和方法。
2.2.2、单根性
若类B继承类A,那么建立类B时只需要再描述与基类(类A)不同的少量特征(数据成员和成员方法)即可。这种做法能减小代码和数据的冗余度,大大增加程序的重用性。
2.2.3、子类调用父类成员
1、使用super关键字调用父类成员
2、子类默认会先调用父类的无参构造方法,如果父没有则报错,可以手动指定,但必须在第一行
super
super() 表示调用父类的无参构造方法
如何调用有参构造呢
1)在父类里建一个有参构造方法
/**动物*/
public class Animal {
/**名称*/
public String name;
/**颜色*/
public String color;
public Animal() {
System.out.println("这是动物类的空构造方法");
}
public Animal(String name, String color) {
this.name = name;
this.color = color;
}
/**显示信息*/
public void show(){
System.out.println("名称:"+name+",颜色:"+color);
}
}
2)在子类的有参构造里先调用父类的有参构造,然后再做独有业务
/**狗继承自动物,子类 is a 父类*/
public class Dog extends Animal {
public Dog(String name, String color,double price) {
super(name,color); //调用父类构造方法
this.price=price; //调用当前对象的成员
}
/**价格*/
public double price;
/**重写父类方法*/
public void show(){
/**子类调用父类成员*/
super.show();
System.out.println("价格:"+this.price);
}
}
/**猫*/
public class Cat extends Animal {
/**重量*/
public double weight;
}
public class Zoo {
public static void main(String[] args) {
Dog dog = new Dog("中华田园犬","蓝色",123.56);
dog.show();
Cat cat = new Cat();
cat.name = "波斯猫";
cat.color = "红色";
cat.weight = 18.5;
cat.show();
A a = new A();
a.y = 100;
B b = new B();
b.y = 200;
// com.nf.c401.Hello h=new com.nf.c401.Hello();
C c = new C();
c.y = 200;
c.z = 200;
}
}
注意:
super()无论是有参还是无参都必须写在子类构造方法的第一行。
如果使用super关键字调用父类构造器,必须写在该子类构造器的第一行
super和this关键字
super:指向父类的引用
super();
this:指向当前类的引用
this();
课堂练习
练习2(2.1,2.2前两个问)
2、设计2个类,要求如下: [必做题]
2.1 定义一个汽车类Vehicle,
2.1.1 属性包括:汽车品牌brand(String类型)、颜色color(String类型)和速度
speed(double类型)。
2.1.2 至少提供一个有参的构造方法(要求品牌和颜色可以初始化为任意值,但速度的初始值必须为0)。
2.1.3 为属性提供访问器方法。注意:汽车品牌一旦初始化之后不能修改。
2.1.4 定义一个一般方法run(),用打印语句描述汽车奔跑的功能
2.1.5 在main方法中创建一个品牌为“benz”、颜色为“black”的汽车。
2.2 定义一个Vehicle类的子类轿车类Car,要求如下:
2.2.1 轿车有自己的属性载人数loader(int 类型)。
2.2.2 提供该类初始化属性的构造方法。
Vehicle类
public class Vehicle {
private String brand="audi";
private String color="red";
private double speed=0;
public Vehicle(String brand, String color, double speed) {
super();
this.brand = brand;
this.color = color;
this.speed = speed;
}
public void run() {
System.out.println("一辆颜色为" + color + "的" + brand + "以初始速度" + speed + "起步");
}
public Vehicle() {
super();
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public double getSpeed() {
return speed;
}
public void setSpeed(double speed) {
this.speed = speed;
}
public String getBrand() {
return brand;
}
}
Car类
public class Car extends Vehicle {
private int loader;
public Car() {
super();
}
public Car(String brand, String color, double speed, int loader) {
super(brand, color, speed);
this.loader = loader;
}
}
问题引出:
2.2.3 重新定义run(),用打印语句描述轿车(Car)奔跑的功能。
多态:
1)重写:子类继承父类之后,重新书写父类的方法
关键词:override
如果重写:右键-source-override/implements methods-选中要重写的方法-ok
执行后自动生成代码如下:
@Override
public void run() {
// TODO Auto-generated method stub
super.run();
}
课堂练习:
2.2.3 重新定义run(),用打印语句描述轿车奔跑的功能。
2.2.4 在main方法中创建一个品牌为“Honda”、颜色为“red”,载人数为2人的轿车。
public class Car extends Vehicle {
private int loader;
public Car() {
super();
}
public Car(String brand, String color, double speed, int loader) {
super(brand, color, speed);
this.loader = loader;
}
@Override
public void run() {
// 颜色 品牌 速度 载人数
System.out.println("一辆" + super.getColor() + "的" + super.getBrand() + "载着" + loader + "个人,以" + super.getSpeed()
+ "公里每小时的速度,在高速公路上飞驰!");
}
public static void main(String[] args) {
// 在main方法中创建一个品牌为“Honda”、颜色为“red”,载人数为2人的轿车。
Car honda = new Car("Honda", "red", 0, 2);
honda.run();
}
}
重写和重载在写法的区别:
重载: 编译期多态 强调类内部的同名方法
重写:运行期多态 强调类和类之间的同名方法
重载:相同的方法名
参数必须不同(参数类型不同或参数个数不同)
返回值可以相同也可以不同
重写:相同的方法名
相同的参数列表(参数类型,参数顺序)
相同的返回值类型
多态的另一种表现形式
https://www.cnblogs.com/V1haoge/p/9558825.html
父类假设有多个方法,他只实现其中的一个或几个,那些没有实现的方法等待子类去实现。
如果这个方法是等待子类去实现的,那么这个方法我们用一个关键字给他标上 abstract
用abstract标识的方法叫抽象方法,他有几个特点(1不需要实现 2 方法只有定义没有方法体)
应用:模板模式应用的就是抽象类。把多个流程给串连起来。
public abstract class UserLogin {
//用户名是否重复
public abstract boolean validateUserName(String userName);
//验证身份证号正确性
public boolean validateUserId(String userID){
//15位或18位
return true;
}
//验证邮箱
public boolean validateUserEmail(String userEmail){
return true;
}
public final boolean validate(){
//1)首先验证用户名是否合法
boolean result1=validateUserName("wang.qj");
//2)如果用户名合法,我再验证身份证号是否合法
if(result1){
boolean result2=validateUserId("210411199609230761");
//3)如果身份证号也合法,我再验证邮箱,如果这三个都验证通过,就返回整个验证结果为true
if(result2){
boolean result3=validateUserEmail("wang.qj@neusoft.com");
if(result3){
return true;
}
}
}
return false;
}
}
子类
public class UserLoginChild extends UserLogin {
@Override
public boolean validateUserName(String userName) {
System.out.println("验证用户名");
return true;
}
public static void main(String[] args) {
UserLoginChild ul=new UserLoginChild();
ul.validate();
}
}
注意中间的final
方法如果被final修饰,不能被子类重写
课后习题1,3
课后习题1
public class Players {
//静态共享的计数器
private static int sum=1;
//构造方法改为私有,只有当前类里的方法可以访问到
//利用封装
private Players() {
// TODO Auto-generated constructor stub
}
public static Players create(){
if(sum<=11){
System.out.println("创建了一个对象");
Players p=new Players();
sum++;
return p;
}else{
System.out.println("对不起,已经创建了11个对象,不能再创建了");
}
return null;
}
public static void main(String[] args) {
for(int i=0;i<20;i++){
Players.create();
}
}
}
课后习题3
/*设计Shape表示图形类,有面积属性area、周长属性per,颜色属性color,
- 有两个构造方法(一个是默认的、一个是为颜色赋值的),还有3个抽象方法,
- 分别是:getArea计算面积、getPer计算周长、showAll输出所有信息,
- 还有一个求颜色的方法getColor
*/
public abstract class Shape {
protected double area;
protected double per;
protected String color;
public Shape() {
}
public Shape(String color) {
this.color = color;
}
public abstract void getArea();
public abstract void getPer();
public abstract void showALL();
public String getColor(){
return this.color;
}
}
/3.2 设计 2个子类:
3.2.1 Rectangle表示矩形类,增加两个属性,Width表示长度、
height表示宽度,重写getPer、getArea和showAll三个方法,
另外又增加一个构造方法(一个是默认的、一个是为高度、宽度、颜色赋值的)。/
public class Rectangle extends Shape{
protected double width;
protected double height;
public Rectangle() {
}
public Rectangle(double width, double height,String color) {
super();
this.width = width;
this.height = height;
this.color = color;
}
@Override
public void getArea() {
area=width*height;
}
@Override
public void getPer() {
per=(width+height)*2;
}
@Override
public void showALL() {
System.out.println("这是一个面积为"+area+"周长为"+per+"颜色为"+color+"的矩形");
}
}
/*3.2.2 Circle表示圆类,增加1个属性,radius表示半径,
* 重写getPer、getArea和showAll三个方法,
* 另外又增加两个构造方法(为半径、颜色赋值的)。
*/
public class Circle extends Shape {
protected double radius;
public Circle(double radius ,String cloor) {
super();
this.radius = radius;
this.color = cloor;
}
@Override
public void getArea() {
area=radius*radius*3.14;
}
@Override
public void getPer() {
per=2*radius*3.14;
}
@Override
public void showALL() {
System.out.println("这是一个面积为"+area+"周长为"+per+"颜色为"+color+"的圆形");
}
}
/测试类中,在main方法中,声明创建每个子类的对象,并调用2个子类的showAll方法/
public class ShapeTest {
public static void main(String[] args) {
Circle cc=new Circle(3, "黑色");
cc.getArea();
cc.getPer();
cc.showALL();
Rectangle rt=new Rectangle(7,8,"红色");
rt.getArea();
rt.getPer();
rt.showALL();
}
}
接口interface
接口中只包含常量和抽象方法,而没有变量和方法的实现
接口不是一个类,不能实例化
接口对类来说是一套规范,是一套行为协议;
如何创建?
new-interface-接口名称
public interface ITest {
//接口里面所有的方法都是抽象方法,没有实现
public void add();
public void update();
public void del();
public void findAll();
}
接口如何被实现呢?
public class TestImpl01 implements ITest {
@Override
public void add() {
// TODO Auto-generated method stub
}
......
}
一个类实现多个接口,再新建一个接口
public interface ITest2 {
public void findByID();
}
新建一个类实现上ITest,ITest2
public class TestImpl03 implements ITest, ITest2{
public TestImpl03() {
// TODO Auto-generated constructor stub
}
@Override
public void findByID() {
// TODO Auto-generated method stub
}
@Override
public void add() {
// TODO Auto-generated method stub
}
@Override
public void update() {
// TODO Auto-generated method stub
}
@Override
public void del() {
// TODO Auto-generated method stub
}
@Override
public void findAll() {
// TODO Auto-generated method stub
}
}
课堂练习 34页
Person类
public class Person {
private String name;
private int age;
private String sex;
public Person() {
// TODO Auto-generated constructor stub
}
public Person(String name, int age, String sex) {
super();
this.name = name;
this.age = age;
this.sex = sex;
}
public String getInfo() {
return name + age + sex;
}
public void sayHello() {
System.out.println("hello");
}
}
Consumer接口
public interface Consumer {
public void useCredit();
}
Student类
public class Student extends Person implements Consumer {
private String school;
public Student() {
// TODO Auto-generated constructor stub
}
public Student(String name, int age, String sex, String school) {
super(name, age, sex);
this.school = school;
}
@Override
public void useCredit() {
// TODO Auto-generated method stub
System.out.println("信用良好!");
}
public void study(){
System.out.println(super.getInfo()+"在"+school+"学习");
}
public static void main(String[] args) {
Student fyl=new Student("范迎利",21,"男","东软");
fyl.study();
fyl.useCredit();
}
}
final
final意义:最终的,不可改变的。
修饰变量,为常量,值不可变(修饰基本类型时值不可变);
修饰对象(引用类型),值可变,引用地址不变;
修饰方法,方法不可重写;
-
修饰类,无子类,不可以被继承,更不可能被重写。
5.static final修饰的变量,变量名全大写
练习:
在java中,使用代码定义如下:public class Test {
private static final int[] a = {10};
public static void main(String[] args) {
a[0] = 20;
}
}
请判断代码是否正确,如果错误,请指出问题 ______________________(如果正确,请在横线处填入 正确,如果错误,请在横线处填入 如何解决)
答案是正确,原因如上2
static 更强调是共享 final强调不可改变
接口与抽象类的对比
1)接口里的方法都是抽象的,而抽象类可以。
从JKD8开始,接口可以有默认方法default和static方法。
public interface ITest {
//接口里面所有的方法都是抽象方法,没有实现
public void add();
public void update();
public void del();
public void findAll();
default void test(){
}
static void test1(){
}
}
2)类可以实现多个接口,但只能有一个父类。(单继承,多实现)
3)接口和接口之间可以多继承(接口可以合并)
- 抽象类可以理解为抽象方法和非抽象方法的混合体,而接口中的方法完全是抽象方法,是一套纯粹的规范。
- 一般来说,有关系的类才能继承同一个抽象类,而无关的类不可能有同一个抽象父类,但是无关的类可以实现同一个接口。
6)接口里的属性必须是static final的,但抽象类无限制
7)接口里不能有构造方法,抽象类可以有构造方法
上溯造型即向上转型:子类向父类转,自动转型。条件是两个类要有继承或者实现关系
父类:
public class Animal {
public void run(){
System.out.println("Animal run");
}
}
子类
public class Dog extends Animal {
@Override
public void run(){
System.out.println("Dog run");
}
public static void main(String[] args) {
//向上转型 从dog---->Animal
//从小向大转型 自动转型
//向上转型时,方法调用的实际上是子类的,因为方法已经被子类重写了。
Animal d=new Dog();
d.run();
}
}
再试验一下父类和子类的属性
父类:
public class Animal {
int i=0;
public void run(){
System.out.println("Animal run");
}
}
子类:
public class Dog extends Animal {
int i=5;
@Override
public void run(){
System.out.println("Dog run");
}
public static void main(String[] args) {
//向上转型 从dog---->Animal
//从小向大转型 自动转型
//向上转型时,方法调用的实际上是子类的,因为方法已经被子类重写了。
Animal d=new Dog();
d.run();
//打印结果为0,来自父类,因为属性不能被重写
System.out.println(d.i);
}
}
发现打印的i=0
证明:父亲的引用指向子类对象,但是属性不被重写。
向下转型:
@Test
public void test(){
//向下转型
//animal-dog
Animal a=new Animal();
Dog d=(Dog)a;
d.run();
}
上面是代码是错的,因为得曾经向上转型过的才能向下转。也就是你得是子类,你才能转成子类。
改动一下上面的代码
@Test
public void test(){
//向下转型
//animal-dog
Animal a=new Dog();
Dog d=(Dog)a;
d.run();
}
下溯造型
向下转型(Downcasting):强制转换
将父类对象显示的转换成子类类型。
曾经向上转换过的对象,才能再向下转换。对象不允许不经过上溯造型而直接下溯造型
instanceof 用来判断对象的类型
obja instanceof objb ---->true/false
public class Animal {
static int i=0;
public void run(){
System.out.println("Animal run");
}
public String getWhoRun(Animal animal){
//如果是小猫,就返回猫
if(animal instanceof Dog){
return "小狗";
}else if(animal instanceof Cat){
return "小猫";
}
//如果是小狗,就返回狗
return "未知动物";
}
public static void main(String[] args) {
Dog.i=6;
System.out.println(Dog.i);
}
}
public class Dog extends Animal {
static int i=5;
@Override
public void run(){
System.out.println("小狗 run");
}
public void new_run(){
System.out.println("new run");
}
public static void main(String[] args) {
//向上转型 从dog---->Animal
//从小向大转型 自动转型
//向上转型时,方法调用的实际上是子类的,因为方法已经被子类重写了。
//向上转型时,子类新扩充的方法,父类是无法知道的
Animal d=new Dog();
d.run();
//打印结果为0,来自父类,因为属性不能被重写
System.out.println(d.i);
}
}
public class Cat extends Animal {
public Cat() {
// TODO Auto-generated constructor stub
}
@Override
public void run() {
// TODO Auto-generated method stub
System.out.println("小猫run");
}
}
@Test
public void test2(){
Animal a=new Dog();
Animal b=new Cat();
String result=a.getWhoRun(a);
System.out.println(result+"在跑");
String result1=a.getWhoRun(b);
System.out.println(result1+"在跑");
}
作业:
1)编程实现、以电话Phone为父类(例、电话有本机号码、打电话、接电话等属性和功能,当然还有一些其它的特性;
移动电话Mobilephone和固定电话Fixedphone为两个子类,并使移动电话实现接口Moveable,接口里有移动信息功能;
固定电话又有子类、无绳电话Cordlessphone。
设计并定义这几个类,明确它们的继承关系,定义子类时给出子类有别于父类的新特性。(可以自已添加)
2)声明测试类、声明Phone类的数组(含5个元素),生成五个对象存入数组、其中二个Phone类的对象、一个Mobilephone类的对象、一个Fixedphone类的对象和一个Cordlessphone类的对象,打印输出每个对象的某个成员变量。将一个父类的引用指向一个子类对象,用这个塑型后的对象来调用某个方法实现多态性。
参考答案
Phone类
public class Phone {
private String Number;
private String color;
private String brand;
public void call(){
System.out.println("打电话");
}
public void getCall(){
System.out.println("接电话");
}
public Phone(String number, String color, String brand) {
super();
this.Number = number;
this.color = color;
this.brand = brand;
}
public String getNumber() {
return Number;
}
public String getColor() {
return color;
}
public String getBrand() {
return brand;
}
}
Mobilephone类
public class Mobilephone extends Phone implements Moveable {
private int screenSize;
public void moveMessage(){
System.out.println("发短信");
}
@Override
public void call() {
System.out.println("移动着打电话");
}
@Override
public void getCall() {
System.out.println("移动着接电话");
}
public Mobilephone(String number, String color, String brand, int screenSize) {
super(number, color, brand);
this.screenSize = screenSize;
}
public int getScreenSize() {
return screenSize;
}
}
Fixedphone类
public class Fixedphone extends Phone {
private String locNum;
@Override
public void call() {
System.out.println("固定地点打电话");
}
@Override
public void getCall() {
System.out.println("固定地点接电话");
}
public Fixedphone(String number, String color, String brand, String locNum) {
super(number, color, brand);
this.locNum = locNum;
}
public String getLocNum() {
return locNum;
}
}
Cordlessphone类
public class Cordlessphone extends Fixedphone {
public Cordlessphone(String number, String color, String brand, String locNum) {
super(number, color, brand, locNum);
}
@Override
public void call() {
System.out.println("在一定范围内移动着打电话");
}
@Override
public void getCall() {
System.out.println("在一定范围内移动着接电话");
}
}
Moveable接口
public interface Moveable {
public void moveMessage();
}
TestPhone类
public class TestPhone {
public static void main(String[] args) {
Phone p[]= new Phone[5];
p[0] = new Phone("13998373955", "黑色", "samsung");
p[1] = new Phone("13998373966", "白色", "apple");
p[2] = new Mobilephone("13998373911", "蓝色", "XiaoMi", 6);
p[3] = new Fixedphone("83739223", "红色", "ChinaTelecom", "024");
p[4] = new Cordlessphone("73922346", "黄色", "ChinaUnicom","0414");
for (int i = 0; i < 5; i++) {
String s = p[i].getBrand();
System.out.println(s);
p[i].getCall();
}
Phone p1 = new Mobilephone("13998373933", "绿色", "oppo", 5);
p1.call();
}
线上学习:单例