继承
1、定义
在Java中定义一个类时,让该类通过 extends 关键字继承一个已经存在的类,同时能够将被继承的父类中设定好的属性和方法直接拿过来使用,这就是类的继承。
(1)被继承的类叫做父类(基类),新的类叫做子类(派生类)。
(2)子类可以继承父类的所有属性和方法,同时也可以增加属于自己的属性和方法。
(3)在继承时,子类不能够继承父类的构造方法,也不能继承父类的私有属性和私有方法。
2、语法格式
[ 修饰符 ] class 子类名 extends 父类名 { }
//父类
public class Father {
String name;
int age;
public Father(String name,int age){
super();
this.name = name;
this.age = age;
}
public Father(){
System.out.println("new Father...");
}
public void eat(){
System.out.println(this.name+"正在吃东西");
}
public void sleep(){
System.out.println(this.name+"正在睡觉");
}
}
//子类
public class Son extends Father{
public Son(){
System.out.println("new Son...");
}
public void eat(){
System.out.println(t"正在吃东西");
}
public void sleep(){
System.out.println("正在睡觉");
}
}
3、规则和优点
(1)Java中只支持单继承,也就是说每个子类只能有一个父类,不能有多继承。
(2)一个父类可以有多个子类。
(3)子类继承父类的所有属性和方法,同时也可以增加属于自己的属性和方法。
(4)不能继承 private 修饰的属性和方法。
(5) 在Java中任何的class都有一个父类 Object 类。
——Object类是所有class的父类。
——Java中只允许单继承。
——若手动添加指定父类,则系统不会默认Object为该class的父类。
——若没有继承任何父类,则系统默认Object为其父类。
(6) 优点:使编码更高效,可以代码重用。
4、子类实例化的过程
(1)子类实例化时要先实例化它的父类,然后在实例化子类。
(2)要先调用父类的构造方法,父类的构造方法运行完毕后,才能调用子类的构造方法。
(3)子类的构造器——子类不能够继承父类的构造器;
——在子类中创建构造器时,必须调用父类的构造器;
——子类可以在自己的构造器中使用 super 关键字来调用父类的构造器;
—— 如果使用super关键字调用父类的构造器,必须写在子类构造器的第一行;
—— 如果调用的是父类的无参构造器,可以不用写super();
—— 如果子类调用了父类的无参构造器,而父类中没有无参构造器,则编译器提示错误。
super(参数1,参数2,...);
5、super 和 this 关键字
(1)super()
—— 作用:调用父类的构造器;
—— 只能出现在子类的构造器中,且必须是放在第一行;
—— super()中的参数,决定了要调用父类的哪个构造器;
—— 如果子类构造器中没有出现 super ,那么编译器会默认加上super(),即调用父类的空构造器,如果父类没有空构造器,编译器提示错误。
(2)this()
—— 作用:调用本类的构造器;
—— 只能写在构造器的第一行;
(3)在同一个构造器中 super()和 this()不能同时出现。
(4)super:指向父类的引用;this:指向本类的引用。
//父类
public abstract class Shape {
private double area;
private double per;
private String color;
public void setArea(double area) {
this.area = area;
}
public void setPer(double per) {
this.per = per;
}
public void setColor(String color) {
this.color = color;
}
public Shape() {
super();
}
public Shape(String color){
super();
this.color = color;
}
public String getColor(){
return color;
}
public abstract double getArea();
public abstract double getPer();
public abstract void showAll();
}
//子类
public class Rectangle extends Shape{
private int width;
private int height;
public int getWidth() {
return width;
}
public void setWidth(int width) {
this.width = width;
}
public int getHeight() {
return height;
}
public void setHeight(int height) {
this.height = height;
}
public Rectangle() {
super();
}
public Rectangle(int width, int height,String color) {
super(color); //调用父类的有参构造方法
this.width = width;
this.height = height;
}
@Override
public double getArea() {
double area = width*height;
return area;
}
@Override
public double getPer() {
double per = (width+height)*2;
return per;
}
@Override
public void showAll() {
System.out.println("Rectangle的长度是:"+width);
System.out.println("Rectangle的宽度是:"+height);
System.out.println("Rectangle的面积是:"+getArea());
System.out.println("Rectangle的周长是:"+getPer());
System.out.println("Rectangle的颜色是:"+getColor());
}
}
//测试类
public class Test {
public static void main(String[] args) {
Rectangle rectangle = new Rectangle(4,5,"black");
rectangle.showAll();
}
}
访问权限修饰符
1、封装
封装就是把客观事物封装成抽象的类,把对象的属性和行为结合在一个独立的类中,并且类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的进行信息隐藏。
隐藏属性、方法或实现细节的过程称为封装。
<1>封装可以隐藏实现细节,使得代码模块化。
<2>让使用者只能通过事先制定好的方法来访问数据,可以方便地加入控制逻辑,限制对属性的不合理操作。
<3>便于修改,增强代码的可维护性。
<4>封装的目的是实现代码重用。
2、访问权限修饰符
Java有四个作用域,访问权限修饰符 ----- 适用于类和属性
(1)public(公有)-----任何位置都可以访问
(2)protected(受保护的)-----在同一个包中的所有类都可以访问,以及该类的子类(可以是不同包)
(3)default(默认的)-----在同一个包中
(4)private(私有的)-----只能在本类中
用来控制类的成员和类的使用范围。
——类成员的访问权限修饰符:public、protected、default、private
——类的访问权限修饰符:public、default
方法的覆盖(重写) —— override
方法的覆盖、重写:子类对从父类继承过来的方法进行改造,重新编写。
——在子类继承父类时发生。
1、规则
在子类中的覆盖(重写)方法和父类中被覆盖(重写)的方法应具有:
<1>相同的方法名
<2>相同的参数列表(参数数量、参数类型、参数顺序都要相同)
<3>相同的返回值类型
<4>子类覆盖(重写)方法的访问权限要不小于(大于等于)父类中被覆盖(重写)方法的访问权限。
//父类
public class Father {
String name;
public void eat(){
System.out.println("正在吃东西");
}
protected String run(){
return "跑步";
}
}
//子类
public class Son extends Father{
String name;
//方法重写,返回值类型必须相同
// public int eat(){
// System.out.println("吃东西");
// return 0;
// }
//方法重写,子类的访问权限要不小于(大于等于)父类的访问权限
public String run(){
return "跑步";
}
}
方法的覆盖(重写)(override) 和方法重载(overload)的区别
方法的覆盖、重写 | 方法重载 |
---|---|
重写发生在两个有继承关系的class中 | 重载发生在一个class中 |
重写中方法名和参数列表必须完全相同 | 重载中方法名相同,但参数列表不同 |
方法重写,返回值类型必须相同 | 方法重载,与返回值类型无关 |
子类覆盖(重写)方法的访问权限要不小于(大于等于)父类中被覆盖(重写)方法的访问权限。 | 方法重载,可以有不同的访问修饰符 |
引用数据类型的转换
1、向上转换(Upcasting)——子类转换成父类,自动转换;
——前提是:具有继承或实现关系;
——向上转换损失了子类新扩展的属性和方法,只可以使用从父类中继承的属性和方法。
2、向下转换(Downcasting):强制转换
——将父类对象显示的转换成子类类型;
//在创建出这个对象(引用)时,该对象的类型是子类
PrintMechine print1 = new HeibaiPrint();
print1.print();
//非基本数据类型,大转小,也强转
//在创建出这个对象时,该对象的类型是子类
//例如:print1 在实例化时是new HeibaiPrint()
HeibaiPrint heibaiPrint = (HeibaiPrint)print1;
heibaiPrint.print();
//创建父类,然后直接将父类转换成子类是不可行的
instanceof运算符
1、 判断某个变量的数据类型(小范围)是否符合某个大范围的数据类型
——变量 instanceof 数据类型
2、判断某个对象(引用)是否属于某个类的一个实例。
——对象 instanceof 类
3、它的返回值数据类型是 boolean 型。
public class PrintMechine {
public void print(){ }
}
public class CaisePrint extends PrintMechine{
@Override
public void print() {
System.out.println("打印出的纸张是彩色的");
}
}
public class HeibaiPrint extends PrintMechine{
@Override
public void print() {
System.out.println("打印出的纸张是黑白色的");
}
}
public class SanDPrint extends PrintMechine{
@Override
public void print() {
System.out.println("打印出的纸张是3D的");
}
}
public class Test01 {
public static void main(String[] args) {
//instanceof:判断某个对象或引用是否属于某个类
// 判断某个变量的数据类型(小范围)是否符合某个大范围的数据类型
//父类的引用 print1 的类型是父类类型
PrintMechine print1 = new PrintMechine();
//父类的引用 print2 的类型是子类类型
PrintMechine print2 = new HeibaiPrint();
//print2 变量能够转换成 HeibaiPrint 类型吗
if (print2 instanceof HeibaiPrint) {
HeibaiPrint heibaiPrint2 = (HeibaiPrint) print2;
heibaiPrint2.print();
}
if (print1 instanceof HeibaiPrint) { //false:不运行方法体
//(HeibaiPrint) print1:将父类类型强制转换成子类类型
HeibaiPrint heibaiPrint3 = (HeibaiPrint) print1;
heibaiPrint3.print();
}
//java.lang.ClassCastException :类型转换异常
}
}
多态 --- Polymorphism
多态:多形性,父类型引用指向子类型的实现,动态改变行为。一个class,随着不同的场景,它的表现形态,会随之发生变化。
多态是具有表现多种形态的能力的特征。
同一个实现接口,使用不同的实例而使用执行不同的操作。
(1)多态的实现条件:
<1>父类的引用,引用派生类(子类)的实例:
<2>父类的引用,调用重写后的方法
<3>在运行调用时形成多态
<4>条件:(1)继承 (2)方法重写 (3)类型转换
(2)多态的目的是实现接口重用
(3)多态的作用,就是为了类在继承和派生的时候,保证使用“家谱”中任一类的实例的某一属性时的正确调用。
(4)多态的优点:
—— 简化代码;
—— 改善代码的组织性和可读性;
—— 易于扩散
public class PrintMechine {
public void print(){ }
}
public class CaisePrint extends PrintMechine{
@Override
public void print() {
System.out.println("打印出的纸张是彩色的");
}
}
public class HeibaiPrint extends PrintMechine{
@Override
public void print() {
System.out.println("打印出的纸张是黑白色的");
}
}
public class SanDPrint extends PrintMechine{
@Override
public void print() {
System.out.println("打印出的纸张是3D的");
}
}
public class Test {
public static void main(String[] args) {
//多态
//父类的引用,引用子类的实例
//父类的引用,调用子类重写后的方法
//在运行调用的同时形成多态
//条件:(1)继承 (2)方法重写 (3)类型转换
//在创建出这个对象(引用)时,该对象的类型是子类
PrintMechine print1 = new HeibaiPrint();
print1.print();
PrintMechine print2 = new CaisePrint();
print2.print();
PrintMechine print3 = new SanDPrint();
print3.print();
//非基本数据类型,大转小,也强转
//在创建出这个对象时,该对象的类型是子类
//例如:print1 在实例化时是new HeibaiPrint()
HeibaiPrint heibaiPrint = (HeibaiPrint)print1;
heibaiPrint.print();
//创建父类,然后直接将父类转换成子类是不可行的
}
}
//创建一个调用方法 父类 引用名
public static void print(ObjectDemo demo){
//父类的引用调用重写后的方法
//父类的引用.重写后的方法()
if (demo.getAge() > 20) {
System.out.println("老张的大儿子叫:" + demo.getName() + " 今年" + demo.getAge() );
}
if (demo.getName().equals("zhangyusheng")){
System.out.println("老张的二儿子叫:" + demo.getName() + " 今年" + demo.getAge());
}
}
//运行调用方法
public static void main(String[] args){
//父类的引用,引用派生类(子类)的实例:父类 引用 = new 子类();
ObjectDemo demo = new SonDemoA();
//父类的引用调用重写后的方法:引用.重写后的方法(); 例如:demo.getName();
System.out.println(demo.getName());
System.out.println(demo.getAge());
System.out.println("==================================");
//在运行调用时形成多态
print(new SonDemoA());
System.out.println("==================================");
print(new SonDemoB());
System.out.println("==================================");
}
}
//父类
class ObjectDemo{
public int getAge(){
return 50;
}
public String getName(){
return "zhangsan";
}
}
//子类继承父类,重写父类的方法
class SonDemoA extends ObjectDemo{
//override:方法重写
public int getAge(){
return 25;
}
public String getName(){
return "zhangxueyou";
}
}
class SonDemoB extends ObjectDemo{
public int getAge(){
return 18;
}
public String getName(){
return "zhangyusheng";
}
}
抽象类 —— abstract class
abstract:抽象
abstract class:抽象类:有抽象方法的类就是抽象类。
1、语法格式
[ 访问权限修饰符 ] abstract class 类名 { ... }
2、抽象方法
抽象方法:只有方法声明,没有方法实现(方法体)的方法。
[ 访问权限修饰符 ] abstract 返回值类型 抽象方法名(参数列表);
//abstract class :抽象类
public abstract class Animal {//抽象类:有抽象方法的类就是抽象类。抽象类中也可以有普通方法
String name;
//构造方法
public Animal(String name){
this.name = name;
}
//非抽象方法
public void sleep(){
System.out.println("睡觉");
}
//抽象方法
//abstract仅仅是对方法的一个声明,并没有具体实现
public abstract void eat();
public abstract void play();
}
3、规则
(1)抽象类不能够被实例化 ———— instantiate:实例化,示例
(2)抽象类中有构造方法。
(3)抽象类中也可以有普通方法。
(4)抽象类中的抽象方法必须在它的子类中被实现,否则该子类只能声明为 abstract 。
(5)抽象方法必须被子类覆盖(重写),抽象方法不能够为 private。
(6)不能够将构造方法、类成员方法声明为抽象方法。
(7)abstract 抽象方法不能够和 static 同用。
4、使用抽象类的情况
(1)当一个类的一个或多个方法是抽象方法时;
(2)当一个类是抽象类的子类,并且没有实现父类的所有抽象方法时,即只实现了部分;
(3)当一个类实现接口,并且不能够为所有抽象方法都提供实现时;
final 关键字
final 关键字可以修饰的元素
1、类:final 修饰的类不能够被继承;
2、变量:final 修饰的变量不能够被重新赋值;
——在声明时直接赋值,或者在构造器中赋值。
——系统不会对final 属性默认的赋初始值。
3、方法:final 修饰的方法不能够在子类中被覆盖(重写),即不能够进行修改。并且不能够被子类继承。
接口 —— interface
1、接口的声明
——是方法的定义和常量值的集合。
——关键字:implements:实现 。
——接口中只有常量和抽象方法,没有变量和方法的实现。
[ 访问权限修饰符 ] interface 接口名 { 接口的成员 }
——接口成员:常量、抽象方法
——接口可以多实现。
public interface InterfaceDemo {
//在接口中不能够声明变量,也不能够定义普通方法
// int a;
// int b;
// public void a(){ }
// public void b(){ }
// 在接口中不能够声明变量,也不能够定义普通方法
// 接口中定义的属性必须是 public static final (常量)
// 接口中定义的方法必须是 public abstract
// 接口中属性必须都是常量。
// 接口中方法必须都是抽象方法。
int a = 0; //接口中属性默认为public static final
public static final int b = 100;
public abstract void a();
public abstract void b();
}
2、注意
(1)接口不是一个类,没有构造器,不能够被实例化。
(2)接口使用 interface 关键字来定义,而不是class。
(3)接口中定义的属性必须是 public static final —— 常量
(4)接口中定义的方法必须是 public abstract —— 抽象方法
(5)在接口中不能够声明变量,也不能够定义普通方法 。
(6)子类可以实现多个接口,在接口之间用“,”分隔写多个接口名。
(7)如果不想实现接口,在实现类中加 abstract 修饰符将类抽象化。
//接口1
public interface Temp {
public static final int a = 0;
public static final int b = 100;
public abstract void a();
public abstract void b();
}
//接口2
public interface Temp2 {
public static final int x = 10;
public static final int y = 20;
public abstract void x();
public abstract void y();
}
//接口的实现类 Impl
public class TempImpl implements Temp,Temp2{
@Override
public void a() {
System.out.println("A");
}
@Override
public void b() {
System.out.println("B");
}
@Override
public void x() {
System.out.println("X");
}
@Override
public void y() {
System.out.println("Y");
}
}
//接口的测试类
public class TempTest {
public static void main(String[] args) {
TempImpl tempImpl = new TempImpl();
tempImpl.a();
tempImpl.b();
System.out.println(tempImpl.a);
System.out.println(tempImpl.b);
tempImpl.x();
tempImpl.y();
System.out.println(tempImpl.x);
System.out.println(tempImpl.y);
}
}
3、接口的意义
(1)接口可以实现方法的定义和方法的实现相分离,降低模块间或系统间的耦合性。
(2)接口可以实现多继承。
4、接口和类的关系
——类实现接口:关键字:implements:实现 。
(1)若想要实现一个接口,必须要编写实现接口的类 Impl 。
(2)如果一个类想要实现一个接口,那么这个类就必须实现这个接口中所有的抽象方法。否则这个类只能声明为抽象类。
(3)多个无关的类可以实现一个接口,一个类可以实现多个无关的接口。
(4)一个类可以在继承一个父类的同时,实现一个或多个接口。
接口和抽象类的区别
接口 | 抽象类 |
---|---|
接口是一个接口 - interface | 抽象类是一个类 - class |
接口可以多实现 | 抽象类只能是单继承 |
接口中必须全部都是抽象方法 | 抽象类中可以有非抽象方法,包括抽象方法,构造方法,普通方法 |
接口中定义的抽象方法必须是 public abstract | 抽象类中抽象方法的访问权限修饰符可以是public、protected、default |
接口中定义的属性必须是 public static final 常量 | 抽象类中可以有普通变量和静态变量(static) |
接口中没有构造方法 | 抽象类中有构造方法 |
内部类
1、内部类——就是定义在另一个类内部的类。
2、注意
(1)内部类可以访问其外部类所有的属性和方法。
(2)无需创建外部类对象,即可从内部类访问外部类的属性和方法。
(3)必须创建内部类的对象,否则无法从外部类访问内部类的属性和方法。
(4)如果内部类有和外部类重名的属性或方法,则内部类的属性或方法会优先使用。
(5)不能够定义static变量。
(6)先创建好外部类,通过外部类的对象(引用)才能创建内部类。
//如果内部类有和外部类重名的属性或方法,则内部类的属性或方法会优先使用。
public class OuterClass2 {
int outer = 100;
double x =3.14;
//内部类
class InnerClass2{
int inner = 200;
double x = 3.1415926;
public void showOuter(){
System.out.println(outer);//外部类变量
System.out.println(x); //内部类变量优先级高
//访问变量同名的外部类变量
System.out.println(OuterClass2.this.x);
}
}
public void showInner(){
InnerClass2 innerClass2 = new InnerClass2();
innerClass2.showOuter();
}
public static void main(String[] args) {
OuterClass2 outerClass2 = new OuterClass2();
outerClass2.showInner();
}
}
3、内部类的访问权限修饰符
——普通类的访问权限修饰符:public、default
——内部类的访问权限修饰符:public、protected、default、private
4、内部类的访问
—先创建好外部类,通过外部类的对象(引用)才能创建内部类
OuterClass outerClass = new OuterClass(); //实例化外部类
InnerClass innerClass = outerClass.new InnerClass(); //实例化内部类
public class OuterClass {
int a;
public void add(){
System.out.println("OuterClass=add");
}
//内部类
class InnerClass{
int b;
public void add(){
System.out.println("InnerClass=add");
}
}
public static void main(String[] args) {
//先创建好外部类,通过外部类的对象(引用)才能创建内部类
OuterClass outerClass = new OuterClass();
outerClass.add();
//通过外部类的对象(引用)创建内部类
InnerClass innerClass = outerClass.new InnerClass();
innerClass.add();
}
}
5、静态内部类
——用 static 标识的内部类为静态内部类
(1)静态内部类作为外部类的静态成员,不能访问外部类非静态成员。
(2)非静态内部类只能定义非静态成员,而静态内部类可以定义静态成员和非静态成员。
(3)使用 OuterStatic.InnerStatic in = new OuterStatic.InnerStatic() 方式实例化静态内部类。
//静态内部类
public class OuterStatic {
int outer = 100;
static int outer2 = 300;
static class InnerStatic{
static int a;
int inner = 200;
public void showOuter(){
//System.out.println(outer);
System.out.println(outer2);
}
}
public void showInner(){
InnerStatic innerStatic = new InnerStatic();
System.out.println(innerStatic.inner);
}
public static void main(String[] args) {
//创建外部类
OuterStatic outerStatic = new OuterStatic();
//创建静态内部类
OuterStatic.InnerStatic in = new OuterStatic.InnerStatic();
in.showOuter();
}
}