JAVA初步学习——第六章 封装、继承、多态

一、封装

面向对象编程语言,需要对现实世界中的事物进行抽象、模拟。现实世界中的对象属性,都是隐藏在对象内部的,外界无法直接操作和修改。

  1. 在类中定义属性的时候,一般需要把属性隐藏起来。
    如果外界需要访问这个属性,那么就提供公共方法对其访问。
public class Student{ 
	//使用private关键字来修饰属性,不允许外部直接访问该属性 
	private String name; 
	//提供公共的setName方法,可以让外部调用该方法给name属性赋值 
	public void setName(String name){ 
		this.name = name; 
	}
	//提供公共的getName方法,可以让外部调用该方法获取name属性的值 
	public String getName(){ 
		return name; 
	} 
}

setXxx方法,是设置指定的xxx属性值用的
getXxx方法,是获取指定的xxx属性值用的

  1. private 修饰符的特点:
  • private是四种权限修饰符中的一种,并且是权限最小的一种
  • private可以修饰成员变量和成员方法
  • private修饰属性和方法,就只在当前类中才能访问,在当前类以外的其他地方,都不能访问
  • private修饰的属性,称为类中的私有属性,private修饰的方法,称为类中的私有方法
  1. 封装的优点:
  • 提高代码的安全性,重要信息可以私有化,不对外暴露
  • 提高代码的复用性,常用的代码或者功能封装到方法中,可以在其他地方反复调用
  • 封装代码的实现细节,便于修改内部代码,提高可维护性
  • 简化外部的调用,便于调用者使用

在定义一个类的时候,就是在完成封装的过程。

二、方法重载

类中有多个方法,具有相同的方法名,但是方法的参数各不相同,这种情况被称为方法的重载
方法的重载,必须是在同一个类中,并且要求如下:

  • 方法的名字【必须】相同
  • 方法的参数列表【必须】不同
    • 参数的类型不同
    • 参数的个数不同
    • 参数的顺序不同
  • 方法的修饰符、返回类型、抛出异常这些地方没有限制(可以相同,也可以不同,但一般都是相同的)
public class Test{ 
	//参数的个数不同 
	public void test(){} 
	public void test(int a){} 
	public void test(String s1,String s2){} 

	//参数的类型不同 
	public void test(boolean flag){} 
	public void test(double salary){} 

	//参数的顺序不同 
	public void test(int a,String s){} 
	public void test(String s,int a){} 

	//方法的参数可以相同,也可以不同 
	public int test(int a,int b){ 
		return 0; 
	} 
}

三、 创建和初始化对象

public static void main(String[] args){ 
	new Student(); //new Student(),该代码完成了俩个过程,对象的创建和初始化
}
  1. new关键字,给对象申请/分配内存空间,并将对象中的属性进行默认值的初始化,根据属性类型的不同,其默认值分别为:0 0.0 false ‘\u0000’ null
  2. 如果在代码中,还给这些属性进行了显示赋值,那么就会用这个显示赋的值,把之前的默认值给覆盖掉。
    显示赋值: private String name = "tom";
    最开始name的值是null,之后做了显示赋值,name的值就变为tom
  3. 调用类中的构造器,在构造器中也可以对属性进行赋值,这个赋值会把之前默认值或者显示赋值给覆盖掉

注意:这时候对象已经创建出来了,并且属性也有了值(可能是默认值,也可以是显示赋值,也可能构造器中赋的值),如果我们想再设置其他的属性值,可以使用对象访问属性,或者调用方法。

  • 如果是public属性,那么就可以直接使用对象进行访问并赋值。(不推荐
  • 如果是private属性,那么就需要使用对应的setXxx方法进行属性值的的设置。(推荐

四、构造器

  1. 构造器有以下俩个特点:
  • 必须和类的名字保持一致
  • 必须没有返回类型,也不能写void
  1. 构造器的作用:
  • 使用new关键字来创建对象的时候,后面跟的必须是类中存在的构造器
  • 构造器中的代码,在对象创建后会被调用,从而可以完成对象的初始化工作
public class Student{
	private String name; 
	private int age; //构造器 
	public Student(String name,int age){ 
		this.name = name; 
		this.age = age; 
	}
	public static void main(String[] args){ 
		//创建对象的时候使用构造器 
		//并且传参到构造器中,构造器中可以使用这些参数给属性进行初始化 
		Student stu = new Student("tom",20); 
	} 
}
  1. 构造器的重载:
    除了默认的无参构造器之外,在类中还可以对构造器进行重载,让构造器可以接收一些参数,然后使用这些参数进行对象的初始化工作。
  2. 构造器之间的调用:
    使用this关键字,可以在构造器中,调用另一个构造器
public class Student{ 
	private String name; 
	private int age; 

	//无参构造器
	public Student(){ 
		//调用俩参的构造器 
		this("tom",20); 
	}
	
	public Student(String name){ 
		//调用俩参的构造器 
		this(name,0) 
	}
	
	public Student(int age){ 
		//调用俩参的构造器 
		this(null,age); 
	}
	
	public Student(String name,int age){ 
		this.name = name; 
		this.age = age; 
	} 
}
  1. 默认构造器:
    即使在类中没有定义构造器,那么在编译之后,也会自动的生成一个无参构造器,并且构造器中不执行任何代码。这个无参构造器就被称为默认的构造器。
    如果我们在类中定义了一个构造器,那么系统就不在为该类生成无参的默认构造器了。

五、继承

  1. 继承描述的是事物之间的所属关系,这种关系是: is-a 的关系。
    子类继承父类,子类就可以继承父类中定义的属性和方法。
    父类更通用,子类更具体。

多个类中存在相同属性和行为时,将这些内容抽取到单独一个类中,那么这些类(子类)就不需要再定义这些属性和行为,只要继承同一个类(父类),它们就可以直接访问父类中的非私有的属 性和方法。
子类继承父类,使用关键字 extends

  1. 继承的好处:
    提高代码的复用性。
    类与类之间产生了关系(is a),这是使用多态特性的前提。
  2. 子类继承父类,继承了父类中的属性和方法,并可以在子类中访问这些属性和方法:
//定义Person类,作为父类 
public class Person{ 
	String name; 	
	private int age; 
	public void sayHello(){ 
		System.out.println("hello~ I am "+name); 
	} 
}

//定义Student类,作为子类 
class Student extends Person{ 
//这个是子类中单独定义的方法,和父类无关 
	public void hi(){ 
		name = "jack";//访问从父类中继承过来的方法 
		sayHello();//调用从父类中继承过来的方法 
		age = 20;//编译报错,age是父类中的私有属性,子类中不能访问 
	} 
}

父类中的私有属性和方法,子类中不能访问。
注意,父类中的构造器,子类是不能继承的。

  1. java中,类和类之间的继承是单继承:一个类只能有且只有一个父类,不能同时继承俩个父类
  2. java中,如果没有给类指定父类的话,那么这个类会默认继承父类Object

注意,Object类中没有属性。
java中,每个类都是直接或者间接了继承了Object,可以说Object是所有类的父类类型

  1. 子类继承父类,创建子类对象的时候,会先默认调用父类的构造器:
//定义Person类,作为父类 
public class Person{ 
	public Person(){ 
		System.out.println("Person类中的构造器被调用");
	} 
}

//定义Student类,作为子类 
class Student extends Person{ 
	public Student(){ 
		System.out.println("Student类中的构造器被调用"); 
	} 
}
public static void main(String[] args){ 
	new Student(); 
}

//main方法执行会输出以下语句: 
//Person类中的构造器被调用 
//Student类中的构造器被调用

子类继承父类,会继承父类的属性和方法,那么就需要先调用父类的构造器对父类中的属性进行初始化,初始化完成后再给子类使用。
构造器的作用之一就是进行初始化

六、super关键字

在子类中,使用super关键字一般做以下事情:

  • 访问父类中的属性
  • 调用父类中的方法
  • 调用父类中的构造器
  1. 访问父类中的属性:
public class Person{ 
	String name = "zs"; 
}

public class Student extends Person{ 
	//注意,这里是故意和父类中的属性重名 
	String name = "lisi"; 
	public void test(){ 
		//直接使用name,表示Student中的name属性(就近原则)
		System.out.println(name); //也可以使用this和super来区分访问的是哪个name 	
		System.out.println(this.name);//Student中的name 
		System.out.println(super.name);//父类Person中的name 
	} 
}

2.调用父类中的方法:

public class Person{ 
	public void run(){ 
		System.out.println("person run.."); 
	} 
}

public class Student extends Person{ 
	//注意,这里是故意和父类中的方法重名 
	public void run(){ 
		System.out.println("student run.."); 
	}
	public void test(){ 
		//直接使用name,表示Student中的run方法(就近原则) 
		run(); 
		//也可以使用this和super来区分调用的是哪个run方法 
		this.run(); 
		super.run(); 
	} 
}
  1. 调用父类中的构造器:
    (1)子类构造器中隐式调用父类无参构造器
public class Person{ 
	public Person(){ } 
}

public class Student extends Person{ 
	public Student(){ 
		//这里会隐式调用(自动调用)父类的无参构造器 
	} 
}

(2)子类构造器中显式调用父类无参构造器

public class Person{ 
	public Person(){ } 
}

public class Student extends Person{ 
	public Student(){ 
		//也可以使用super关键字,显示调用父类的构造器(有参的无参的都可以调用) 
		super(); 
	} 
}

(3)子类构造器中显式调用父类有参构造器

public class Person{ 
	public Person(){ }
	public Person(String name){ } 
}

public class Student extends Person{ 
	public Student(){ 
		//也可以使用super关键字,显示调用父类的构造器(有参的无参的都可以调用) 
		super("tom"); 
	} 
}
  1. 在构造器中,可以使用this调用类中其他构造器,也可以使用super调用父类中的构造器。但是this和super这俩种调用构造器的代码,不能同时出现,否则会报错。
  2. 如果this和super不是都要调用构造器,那么同时出现就没有问题

七、方法重写

如果子类和父类中出现了相同的方法,这种情况就叫做方法重写。
注意,方法重载是发生在同一个类中,方法重写发生在子父类之间

  1. 父类中的一个方法和子类中的一个方法,满足以下要求,就是方法的重写:
  • 方法名【必须】相同
  • 参数列表【必须】相同
  • 访问控制修饰符可以被扩大,但是【不能】被缩小
    public > protected > default > private
  • 方法抛出异常类型的范围可以被缩小,但是【不能】被扩大
    例如:ClassNotFoundException --扩大–> Exception
    例如:Exception --缩小–> ClassNotFoundException
  • 返回类型可以相同,也可以不同
    • 如果父类的返回类型是引用类型,子类重写后的方法返回类型可以和父类方法的返回类型保持一致,也可以是父类方法返回类型的子类型
      例如,父类方法的返回类型是Person,子类重写后的返回类可以是Person也可以是Person的子类型
    • 如果父类的返回类型是基本类型,那么子类重写后的返回类型【必须】和父类的保持一致
      例如: 父类方法的返回类型是int,子类重写后的返回类也必须是int

注意,大多数情况下,子类中重写的方法 会和 父类中的方法 完全保持一致,只有方法的实现不同。(也就是大括号中代码不一样)
2. 父类中哪些方法不能被重写:

  • 父类中的静态方法不能被子类重写
  • 父类中的私有方法不能被子类重写

只有在子类中,可以直接访问到的,父类的方法,并且是非静态的方法,才能被子类重写

public class Person{ 
	public void sayHello(){ 
		System.out.println("你好!很高兴认识你"); 
	} 
}

public class Student extends Person{ 
	//子类中,重写父类的sayHello方法 
	public void sayHello(){ 
		System.out.println("hello!nice to meet you"); 
	} 
}

public static void main(String[] args) { 
	Student stu = new Student(); 
	//由于子类重写了sayHello方法,所以这里将会调用到子类中重写后的sayHello 
	//如果子类中没有重写sayHello方法,那么这里将会调用到从父类继承过来的sayHello方法 
	stu.sayHello(); 
}

子类继承父类,在调用方法的时候,如果子类中没用重写,那么调用的是从父类继承的方法,如果子类重写了这个方法,那么将会调用到子类中重写后的方法。( 非常重要 )

八、多态

相同类型的不同对象,调用同一个方法,最终执行结果是不同的
不同的对象,进行同一个行为,但是它们的表现出来的确实不同的形态。java中的多态,就是来描述这种情况的。

  1. 多态的前提:
  • 子类继承父类
  • 子类重写父类中的方法
  • 父类的引用指向子类对象

类实现接口,这是一种特殊形式的继承,多态也可以体现在类和接口的关系中。
一个父类型的引用,可以指向它的任何一个子类对象

public class Animal { 
	public void eat(){} 
}
class Cat extends Animal { 
	public void eat() { 
		System.out.println("我是喵咪,我喜欢吃鱼"); 
	} 
}
class Dog extends Animal { 
	public void eat() { 
		System.out.println("我是狗仔,我喜欢吃骨头"); 
	} 
}
public static void main(String[] args) { 
	//多态形式,创建对象 
	//父类引用指向子类对象 
	Animal a = new Cat(); 

	//调用的是Cat中重写的eat方法 
	//因为这时候引用a指向的是Cat类型对象 
	a.eat(); 

	//父类引用指向另一个子类对象 
	a = new Dog(); 

	//调用的是Dog中重写的eat方法 
	//因为这时候引用a指向的是Dog类型对象 
	a.eat(); 
}
  1. 程序中使用多态的好处:
    (1)不使用多态
public class Game { 
	public void start(BasketBall basketBall){ 
		basketBall.play(); 
	}
	public void start(Football football){
		football.play(); 
	}
	//Game中新增运行乒乓球游戏的方法 
	public void start(PingPangBall pingPangBall){ 
		pingPangBall.play(); 
	} 
}

class BasketBall{ 
	public void play(){ 
		System.out.println("篮球游戏开始初始化..."); 
	} 
}

class Football{ 
	public void play(){ 
		System.out.println("足球游戏开始初始化..."); 
	} 
}

//新增乒乓球类 
class PingPangBall{ 
	public void play(){ 
		System.out.println("乒乓球游戏开始初始化..."); 
	} 
}

public static void main(String[] args) { 
	Game game = new Game(); 
	
	BasketBall basketBall = new BasketBall(); 
	game.start(basketBall); 
	
	Football football = new Football(); 
	game.start(football); 

	//新增创建乒乓球对象 
	PingPangBall pingPangBall = new PingPangBall(); 
	//新增调用game中的对应的运行乒乓球游戏的方法 
	game.start(pingPangBall); 
}

(2)使用多态

public class Game { 
	//这里只需要定义一个方法,参数类型是Ball类型 
	//这个Ball是父类,它的引用可以指向任何一个子类对象 
	public void start(Ball ball){ 
		ball.play(); } 
	}
	
	//父类 
	class Ball{ 
		public void play(){} 
	}
	
	//子类 
	class BasketBall extends Ball{ 
		public void play(){ 
			System.out.println("篮球游戏开始初始化..."); 
		} 
	}

	//子类 
	class Football extends Ball{ 
		public void play(){ 
			System.out.println("足球游戏开始初始化..."); 
		} 
	}

	/*整个代码中,基本只有这里是新增的,其他地方不需要改动*/ 
	/*新增乒乓球类*/ 
	class PingPangBall extends Ball{ 
	public void play(){ 
		System.out.println("乒乓球游戏开始初始化..."); 
	} 
}

public static void main(String[] args) { 
	Game game = new Game(); 
	//声明父类的引用 
	Ball ball; 
	//指向一个子类对象 
	ball = new BasketBall(); 
	//这里想要切换游戏,只需要让引用ball执行对应的子类对象即可 
	//ball = new FootBall(); 
	//ball = new PingPangBall(); 
	game.start(ball); 
}

九、instanceof

instanceof关键字能告诉我们,当前父类的引用,到底是执行的哪一个子类对象。

public void test(Person p){ 
	if(p instanceof Student){ 
		//说明p指向的是Student类型对象 
	}else if(p instanceof Teacher){ 
		//说明p指向的是Teacher类型对象 
	} 
}

十、引用类型的转换

多态中的转型分为向上转型与向下转型两种:

  • 向上转型(子类转父类)
    多态本身就是将子类的对象赋值给父类的引用,这就是一个自动向上转型的过程(类型自动转换
    例如, Person p = new Student();
  • 向下转型(父类转子类)
    父类类型向子类类型转换,是向下转换的过程。(类型强制转换
    Person p = new Student();
    Student s = (Student)p; //向下转型
    (1)在使用多态的情况下,父类引用是无法调用到子类中独有的方法的
public class Person { 
	public void sayHello(){} 
}
class Student extends Person{ 
	//注意,这是子类中单独定义的方法,和父类无关 
	public void study(){} 
}
public static void main(String[] args) { 
	//多态,父类的引用指向子类对象 
	Person p = new Student(); 
	p.sayHello();//编译运行都没问题,子类继承了父类中sayHello方法 
	//编译报错 
	//因为引用p是Person类型的,而Person类型中就没有定义这个study方法 
	//编译器检查到Person中没有这个方法就直接报错了 
	p.study(); 
}

(2)在这个情况下,只能将引用p向下转型,转成Student类型,才能调用到study方法:

public static void main(String[] args) { 
	//多态,父类的引用指向子类对象 
	Person p = new Student(); 
	p.sayHello();//编译运行都没问题,子类继承了父类中sayHello方法 
	Student s = (Student)p; 
	//编译通过 
	//因为引用s是Student类型的,而Student类型中真的有这个study方法 
	s.study(); 
}

(3)引用类型强制转换时的异常:

public class Person { }
class Student extends Person{ 
	//注意,这是子类中单独定义的方法,和父类无关 
	public void study(){} 
}
class Teacher extends Person{ }
public static void main(String[] args) { 
	Person p = new Teacher(); 
	//编译通过,运行报错! 
	//错误类型是:ClassCastException 
	Student s = (Student)p; s.study(); 
}

上面代码运行报错的原因,是因为运行过程中,强制转换的时候,引用p实际指向的对象是Teacher类型的,在这种情况下,它是不能转为Student对象的,因为Teacher对象和Student类型没有任何子父类的关系!
那么这句代码 Student s = (Student)p; 想转换成功,最终运行不报错,只有一种情况,那就是在转换的时候,引用p指向的对象,真的就是Student类型的对象才可以。

(4)使用关键字instanceof进行判断是否引用p指向的对象真的就是Student类型

public static void main(String[] args) { 
	Person p = new Teacher(); 
	//如果引用p真的是指向Student类型的对象,那么才进行类型的强制转换 
	//避免在强制转换的过程中出现ClassCastException类型的异常 
	if(p instanceof Student){ 
		Student s = (Student)p; 
		s.study(); 
	} 
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值