【Java】Java学习笔记3-类 封装继承与多态

类和类之间的关系

依赖关系

​ 一个类充当另一个类的形式参数,可以形容成一个类使用到另外一个类 use,临时性的,关系非常弱。

class Person{
	void sleep() {
	}
	void traval(Bus bus) {
		System.out.println();
	}
	void run(Place p) {	
	}
}
class Bus{
	String noString;
}
class Place{
}

关联关系

​ 源于依赖关系,因为依赖关系类和类之间的关系十分松散,有的时候,需要经常性使用到一个类,可以使用关联关系解决。

class Card{
	void a() {

}

}
class ATM{
	Card c;
	void withdraw(double money) {
		this.c.a();
	}
	void despost(double money) {

}

}

关联关系的方向

​ 分为两种情况:单向关联,双向关联

​ 单项关联:A关联B A中有B作为属性,但是B中没有A作为属性

​ 双向关联:A关联B B关联A

​ 关联的好处:可以通过关联,类调用关联的方法

class Person{
	Phone phone;
	void a() {
		this.phone.call();
	}
	void b() {
		

}

}
class Phone{
	Person person;
	void call() {
		

}

void c() {
		this.person.b();
	}
}

关联关系的多重性

​ 一对一

​ 一对多

class Person{//一对一
	PubClass pubclass;
    String name;//和上面一样
}
class PubClass{//一对多
	Person[] persons=new Person[10];
    //成员变量
    
}

关联关系的特殊情况

​ 【组合】对于关联关系的类,可以在声明的同时进行初始化(new 对象)

​ Person类创建对象的时候,一定会创建一个Pubclass的对象

class PubClass{//一对多
}
class Person{//一对一
	PubClass pubclass=new PubClass();
	String name;
	int age;
}

​ 【聚合】不是在声明的同时进行初始化,而是在使用之前初始化

​ Person类在创建对象的时候,Pubclass不一定创建了对象

class PubClass{//一对多
}
class Person{//一对一
	PubClass pubclass;//空引用
	String name;
	int age;
}

关联关系与依赖关系的区别

​ 关联关系:属性的角色,比较紧密,has,长久

​ 依赖关系:形式参数的角色,比较松散,use,短暂

继承关系

实现关系

访问权限

概念

​ 包package类似于操作系统中的文件夹,提供独立的命名空间

作用

​ 1)将相关的类组织在一起。

​ 2)可以提供独立的命名空间,解决命名冲突

​ 3)包可以提供访问权限的控制

声明方式

​ 每个java文件第一行,都会指明当前类所在的包,如果有子包,会使用.分隔

​ 一个源文件只能有一条包声明的语句

​ 【语法】package 包.子包…

​ 【全限定名】是一个类真实的名字[包.子包…类名]

了解:

​ 包的级别

​ 第一级别:按照项目类型划分 com gov org

​ 第二级别:开发者或者运行商的名称 com.huawei

​ 第三级别:项目名称 erp student

​ 第四级别:模块的名称 teacher

​ com.huawei.student.teacher

引用包中的类

1.import关键字

​ 【规则】

​ 对于包内的类,可以使用类的简单名称(不是全限定名)就可以访问

​ 对于包外的类,需要使用两种方式:

​ 1)使用全限定名

​ 2)import导入相关的类,再使用导入后的简单名称

​ import动作为增加编译时间,不会增加执行时间,因为导入是在编译期间完成的。一般来说可以有很多个import,要求位置必须在package之后,会在class之前。

​ import 包…指定类

​ import 包.*:可以导入包下所有的类。(但是不导入包中的类)

2.improt static关键字(了解)

​ 导入类型中声明的静态成员,导入之后就像在自己代码中定义了变量一样

​ 注意:容易造成命名冲突,原来被导入的方法和属性被覆盖

访问权限修饰符

​ 虽然我们可以使用import导入其他类,但是不是全能的,也要受到权限的限定,java中有4种访问权限修饰符,从大到小:

​ public:公有:谁都可以调用【类、属性、方法、构造器】

​ protected:受保护:同包可以访问,可以被自己类里的函数调用,可以被继承的类调用,不能被其他类调用【属性、方法】

​ default:没有写权限的时候,就是default:默认权限,只能被同包中的类访问【类、属性、方法】

​ private:私有,只有本类中可以访问【属性、方法】

​ 成员变量私有化是封装性很好的体现

面向对象特征

封装

​ 含义:隐藏,隐藏具体实现的细节,只提供给外界调用的统一接口

​ 快捷键shift+alt+s

​ 【封装目的】1)保护数据的安全性 2)方便调用者调用

​ 成员变量私有化是封装性很好的体现

​ 1.【成员私有化的方式】

​ 使用private权限修饰成员变量

​ 2.【成员私有化后的效果】

​ 被私有化的成员,只有在类的内部可以被访问,在类的外部不能被访问

​ 3.【希望访问私有成员方式】

​ 需要提供对外的统一接口(提供统一的public权限的get(获取)或set(赋值))

继承

1.基本情况

​ 背景:经过需求分析,创建两种类JavaTeacher PythonTeacher

​ 属性和行为

​ JavaTeacher:名字、年龄;自我介绍、讲课

​ PythonTeacher:名字、年龄;自我介绍、讲课

​ 【使用继承实现代码的重复】

​ 通过继承:实现代码的重用性,相当于设计类的时候将多个类公共的部分(普通的部分),放到父类中,让子类继承父类,相当于子类中会有父类下成员(private的成员不继承,构造器不继承)

2.实现

​ 继承实现一般和特殊的关系

​ 一般:父类

​ 特殊:子类(拓展类、衍生类)

​ 继承之后的效果:子类下会具有父类下的成员,如果子类需要更高级的功能,可以单独实现

1)继承的类型

​ 隐式继承:当一个类没有显示的继承任何类的时候,每个java类都会默认继承object

​ 显示继承

​ 语法:在定义类的时候,使用 子类名 extends 父类名

​ java中一个类只能继承一个父类(单继承),可以多层继承 孙子继承儿子,儿子继承爷爷

重要操作符instanceof

​ 使用方法:对象 instanceof 类

​ 作用:判断这个对象是不是由这个类(父类)创建的对象

3.成员的继承

​ 子类继承父类的时候,可以继承成员

​ 【结论】

​ 当子类和父类在同一个包中,子类可以继承父类public protected和default

​ 当子类和父类不在同一个包中,子类可以继承父类public protected

​ 但是所有情况都不能继承private

​ protected成员,如果是同包中的类,子类可以继承父类成员

​ 如果是不同包中,声明public class子类,可以访问到父类下protected成员

4.重写

​ 如果子类认为父类下方法不可用,不够用,不合适,可以自己实现方法——覆盖(重写)父类的方法/在子类下创建和父类的方法名、参数、返回值都相同的方法

​ 【重写的方式】

​ 方法名:相同

​ 参数:相同,1.5之后 有泛型的可擦除类型

​ 返回值:相同 1.5之后,返回父类的子类型也可以

​ 1)void

​ 2)都是基本数据类型,int—int

​ 3)父类方法中返回了Fruit 子类方法也可以返回Fruit

​ ----可替换类型

​ 【重写的作用】:如果子类没有重写父类的方法,调用的时候,默认调用父类下的方法,如果重写父类下的方法,则会调用子类自己的重写方法。

​ 【规则】

​ 1)定义跟父类一样的方法(方法名、参数、返回值都跟父类一致)

​ 2)要求子类方法的权限不能低于父类方法的权限(相同可以)——为了扩充父类的方法和属性

​ 3)参数类型和返回值的泛型(后面讲)

​ 4)子类不能比父类跑出更多的异常(后面讲)

​ 【重写标记】@override

5.成员变量的隐藏

属性—“重写”——本质上不是真的重写,只是隐藏而已

如果在父类中声明了一个成员变量,在子类中也声明一个"同名"的成员变量,当调用子类帝乡时,子类成员会隐藏父类成员

【形式】只要名字相同就是隐藏,无论类型是否相同,都会隐藏

【建议】尽量不要这样设计

class Fruit{
	public int aaa=0;
}
class Apple extends Fruit{
	public String aaa="hello";
}
public class Day7 {
	public static void main(String[] args) {
		Apple a=new Apple();
		System.out.println(a.aaa);
	}
}

6.关于构造器的继承

1)继承中的构造器

​ 构造器不是类的成员,所以不能够被继承,但是可以调用父类的构造器

调用父类构造器的使用,需要使用super关键字,需要super必须放在子类构造器的第一行

​ 创建子类对象的时候,**【一定】**会先运行父类的构造器(换句话说,一定会创建一个匿名的父类对象)

​ 第一:如果子类没有定义构造器,或者子类构造器中没有显示的调用父类其他有参数的构造器,创建子类对象的时候,会先调用父类有参数的构造器,然后再调用自己的构造器

​ 第二:如果子类中super父类的有参数构造器,创建子类对象的时候,会先调用父类有参数的构造器,然后再调用自己的构造器

​ 【笔试陷阱】当子类的构造器中没有显式的调用父类构造器,父类只有有参数的构造器,会出错

​ 【思考】【一定】会先运行父类的构造器?为什么?

​ 原因:在创建子类对象的时候,需要创建里面的成员,有一些成员是在父类下的,只有有父类对象,才能找到父类的成员,所以java底层要求先去创建父类对象

​ 所有的类在产生对象的时候,其实都创建了一个Object的类(object构造器)

​ 【super要求放在第一行】:为了先调用父类的构造器, 再初始化自己的内容,否则放在下面,父类构造器的内容就会覆盖掉子类的内容。

class Fruit{
	public Fruit() {
		System.out.println("父类无参构造器");
	}
}
class Apple extends Fruit{
	public Apple() {
		System.out.println("子类无参构造器");}
	}
public class Day7 {
	public static void main(String[] args) {
		Apple a=new Apple();
}
}
class Fruit{
	public Fruit(){
		System.out.println("父类Fruit无参数的构造器");
	}
	public Fruit(String name){
		System.out.println("父类Fruit有参数的构造器");
	}
	public void show(){
		

}

}
class Apple extends Fruit{
	public Apple(){
//		super();//如果子类中没有明确的调用父类的构造器,相当于在子类构造器中加了这一句
//		super("hello");
		System.out.println("子类Apple无参数的构造器");
	}
}

public class Day7_1_OOP {
	public static void main(String[] args) {
//		 Apple a=new Apple("红富士");
//		 Apple对象下应该创建show方法。
	}
}

7.super

super是在继承关系的类中,代表父类的引用(父类对象的名字)

作用:第一个:放在子类构造器中,放在第一行,用来调用父类构造器

第二个:super.成员变量名:在子类中引用父类的成员变量(可以访问,可以修改(注意权限))

第三个:super.方法名:可以用来在子类中引用父类的方法

【真正的应用场合】在设置父类的私有成员

8.final关键字

背景:一个类认为自己设计的很完善,不希望被扩展(不希望有子类)

final

1)可以修饰属性,编剧变量,但是被修饰的变量都不能修改

2)可以修饰方法,被修饰的方法不许被重写

3)可以修饰类,被修饰的类不能够被继承

​ 一个类声明了final类型,那么它次下面的所有方法都被认为是final的方法

【用法】

(1)final修饰变量(成员变量,局部变量)

【规则】被修饰的变量都不能修改

第一个:修饰成员变量

成员变量需要被初始化,而且一旦被初始化之后,就不能被改动了。

初始化的方式:可以在声明的同时初始化,也可以在构造器中进行初始化,只能任选其一

目的:就是在对象创建之前,将final类型变量确定值

第二个:修饰局部变量

​ 可以使形式参数,在方法中声明的局部变量

static是可以修改的,static声明的成员,多个对象公用一个,只跟类有关,不是不可以修改

final是这正不可以修改

当使用static和final一起声明一个常量的时候,常量只能在声明时被赋值;(因为static的成员只跟类有关,跟类无关,而构造器是为了构造对象)

​ public void show(final int age){

//当调用show方法的时候,形式参数一定回去绑定实际参数,在绑定的同时就赋予值,下面的赋值就会变成修改

age=10;

}

class Apple{
//private final String name="aaa";
private final String name;
private static final int V=123;
public Apple(){
this.name="bbb";
//System.out.println(this.name);
}
public void show(final int age){
//当调用show方法的时候,形式参数一定会去绑定实际参数,在绑定同时就赋予值。
//下面赋值就会变成修改
//age=10;
final String type="hello";
//type="new"; final局部变量不可以再次修改
}
}

2)final修饰方法

【规则】使用final修饰的方法不可以被重写

class Fruit{
	public final void show(){
	}
}
class Apple extends Fruit{
//public final void show(){不能被重写
//	
//}
}

3)使用final修饰的类不能被继承

【规则】使用final修饰类,类不能被继承,所有父类的方法都被认为是final类型的方法

final class Fruit{
	public  void show(){
	
	}
}
class Apple extends Fruit{
	public  void show(){
	
	}
}

9.引用类型之间的转换问题

【规则】引用类型之间,如果毫无关系的引用类型之间根本无法转换(包括隐式转换和强制转换都不可以

如果有父子关系继承的类,从父类型赋值给子类类型,可以经历强制转换(但是有条件)

子类对象赋值给父类类型(低->高),可以的,隐式转换。

class Fruit{
}
class Apple extends Fruit{
}
public class Day7_1_OOP {
	public static void main(String[] args) {
		Fruit f=new Fruit();
		Apple a=new Apple();
		

	//没有任何关系的类型之间不能转换

//		String s=(String)f;
//		String s="hell";
//		Fruit f=(Fruit)s;
		f=a;//因为有这个赋值,将a指向的对象给f,a存储的地址给f中存储的地址  f真的变成了子类对象
		a=(Apple)f;
		Apple a2=(Apple)f;
		Fruit f2=a;
	}
}

【父类引用指向子类对象】(非常用)

既然f=a 可以使用父类引用f接住子类对象a

可不可以直接创建一个父类引用,使用子类对象给引用赋值

【父类引用指向子类对象】结果

如果调用这种对象下的方法,如果在子类中重写了,则调用的是重写之后的放阿飞

如果在子类中没有冲洗额,则调用的是父类下的方法

【父类引用指向子类对象】说法:使用子类创建了父类类型对象

向上造型:上(父):将子类对象硬造成父类的类型—【父类引用指向子类对象】

向下造型:下(子):将父类对象硬造成子类类型(需要有条件,需要提前将对象转换成子类对象)

class Fruit{
public void show(){
	System.out.println("水果的show");
}
}
class Apple extends Fruit{
public void show(){
	System.out.println("苹果的show");
}
public void j(){
	System.out.println("苹果酱");
}
}
public class Day7_1_OOP {
public static void main(String[] args) {
//	【父类引用指向子类对象】(非常用)
	Fruit f=new Apple();
	f.show();
//	Apple a =new Fruit();不可以:如果Apple下有父类没有的方法或者属性,a.特殊的方法。
}
}

多态

​ 就是一种对外的表现形式,内在具有多种具体的实现

​ java中多态的具体体现

​ 1)方法的重载:方法名是一个,但是实现的功能不同

​ 2)方法的重写:父类中方法和子类中方法名字、参数、返回值想听,但是实现的功能不同

​ 3)多态参数:

​ 1.父类引用指向子类对象

​ java如果需要执行,需要经历两个时期,一个是编译期,一个是运行期

​ 创建一个引用类型的时候T t=new T();

​ =左边的部分是编译期:指定类创建的类型(为了留出空间)

​ =右边的部分是运行期:真正要创建的对象

​ 当程序中出现了对象,调用对象下的属性或者放方法的时候:

​ 【t.attrfun】

​ 程序会判断时期,根据对象中的属性或者方法产生的时期不同,去调用不同的内容

​ 编译期产生:attrfun名字在编译期是否可以调用的到,要检测是否符合编译期的类型

​ 运行期产生:attrfun名字在运行期是否可以调用的到,要检测是否符合运行期的类型

​ 1)运行期和编译期的类型相同

​ A a=new A();

​ 当声明a的时候,如果声明的是A类型,编译期是处于A类型,当访问属性或者方法,是否满足编译期类型中声明的内容

​ a.show();检测show方法是否在A中存在。

​ 在创建真正的对象的时候,执行的是构造器,在运行期执行,当访问属性或者是方法,就会去运行期类型中检测属性或者方法是否存在。

​ 编译期产生的内容:属性(静态和费静态)、静态的成员

​ 运行期产生的内容:方法、构造器、实例块

​ 2)运行期和编译期的类型不相同(父类引用指向子类对象)

​ Father f=new Son();

​ 按照上面的理论

​ 当我们访问属性,静态方法的时候,回到Father类中验证有效性

​ 当我们访问方法,构造器,实例块,会到Son类中验证有效性,执行Son中的内容

​ 2.验证成员的调用

​ 块、静态成员、实例成员

​ 在创建父类引用指向子类对象的时候,他们全部都执行

​ 【结论】

​ 如果有父类引用指向子类对象:

​ 当调用子类对象的相关成员时:

​ 如果是属性、静态成员:会调用到父类的属性和静态成员

​ 如果是方法、构造器:会调用到子类的方法或者构造器

1)静态块和实例块

子类构造器在调用的时候,一定会去调用父类的构造器

class Fruit{
static{
	System.out.println("父类下的静态块");
}
   {
	System.out.println("父类下的实例块");
   }
}
class Apple extends Fruit{
    static{
		System.out.println("子类下的静态块");
	}
   	{
	System.out.println("子类下的实例块");
	}
}
public class lz{
    public static void main(String[] args){
        Fruit f=new Apple();//子类构造器在调用的时候,一定会去调用父类的构造器
    }
}
/*结果
父类下的静态块
子类下的静态块
父类下的实例块
子类下的实例块
*/

2)非静态成员的调用(常用)

方式是在运行期创建的,执行的时候,执行运行期对象下的方法

class Fruit{
    public void show(){
        System.out.println("父类下的show");
    }
}
class Apple extends Fruit{
   public void show(){
        System.out.println("子类下的show");
    }
     public void j(){
        System.out.println("子类下的j");
    }
}
public class lz{
    public static void main(String[] args){
        Fruit f=new Apple();
        //f.j();编译不通过
        f.show();//方式是在运行期创建的,执行的时候,执行运行期对象下的方法
    }
}
/*结果
子类下的show
*/

3)静态方法的调用

因为静态方法是在编译期产生的,所以执行的时候执行编译期的方法

class Fruit{
public static void show(){
        System.out.println("父类下的show");
    }
}
class Apple extends Fruit{
   public static void show(){
        System.out.println("子类下的show");
   }
}
public class lz{
    public static void main(String[] args){
        Fruit f=new Apple();
        f.show();//因为静态方法是在编译期产生的,所以执行的时候执行编译期的方法
    }
}
/*结果
父类下的show
*/

4)成员变量

属性来说,即使子类下有覆盖父类的属性,访问【父类引用指向子类的对象】的时候,访问的也是父类的属性

原因就是因为属性是编译期产生的

class Fruit{
public String name="水果";
public void setApple(){
}
}
class Apple extends Fruit{
//public String name="苹果";
public String name;
public Apple(){
	 this.name="苹果";
}
public void setApple(){
	 this.name="苹果";
	 System.out.println(this.name);
}
}
public class Day7_1_OOP {
public static void main(String[] args) {
	Fruit f=new Apple();
	f.setApple();
	System.out.println(f.name);//永远访问的都是父类(编译期类型)下的属性
}
}
/*结果
苹果
水果
*/

对于方法:有重写

对于属性:隐藏(对于属性的“重写”–本质不是重写,是隐藏)

能否实现java中的多态,是重写和隐藏的本质区别

重写可以实现多态,会根据运行时对象的真正类型来决定调用哪一个成员,

隐藏是不能实现多态,永远都执行的是编译期的属性

3.多态参数

对于父类引用指向子类对象的【间接】使用

javaTeacher pythonTeacher 下面都是teach方法

背景:需求,希望Test类,Teacher(父类),通过和依赖关系实现了和类之间的引用

class PyhtonTeacher{
	public void teach(){
		System.out.println("python上课");
	}
}
class JavaTeacher{
	public void teach(){
		System.out.println("java上课");
	}
}
public class Day7_1_OOP {
	//public void record(PyhtonTeacher pt,JavaTeacher jt){
		//希望调用到PyhtonTeacher   JavaTeacher下的teach方法。
		//pt.teach();
	//	jt.teach();
	//}
	public void record(PyhtonTeacher pt){
		pt.teach();
	}
	public void record(JavaTeacher jt){
		jt.teach();
	}
	
	public static void main(String[] args) {
		
	}
}

【多态参数的定义】将形式参数定义为一个父类的类型,各个子类中都重写父类中的方法,在方法调用的时候,实际参数传入的是哪一个子类的对象,就会调用到哪一个子类对象下的方法。

将这个形式称为:多态参数

实际参数会给形式参数赋值:

变量名绑定变量名一样。

T t=new T();

T t2=t;

class Teacher{
	public void teach(){
	}
}
class PyhtonTeacher extends Teacher{
	public void teach(){
		System.out.println("python上课");
	}
}
class JavaTeacher extends Teacher{
	public void teach(){
		System.out.println("java上课");
	}
}
public class Day7_1_OOP {
	public void record(Teacher t){
//		相当于经历了  Teacher t=pt;    父类引用  t=子类对象
		t.teach(); //会调用到子类对象下的方法
	}
	public static void main(String[] args) {
		Day7_1_OOP test=new Day7_1_OOP();
		PyhtonTeacher pt=new PyhtonTeacher();
		JavaTeacher jt=new JavaTeacher();
		test.record(pt);
		test.record(jt);
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值