简介
Java是面向对象编程的一种语言,我们可以把Java想象成一个世界,世界中有很多的东西比如人,物品等等,而这些在Java的世界中就是一个一个对象的形式存在的,我们可以把一个对象就理解成一个人,一个物品等等。所以说面向对象的三大特性是我们在学习java中最重要,也是最核心的基础,本篇文章就以简单易理解的方式解读面向对象的三大特性。
封装:
举一个例子:现在我们在Java的世界中,你有一个空调,这个空调你只能看到他外边的样子,他里面的零件是如何组成的你看不到对吧。我们就可以这么理解封装,封装就是把内部的零件不会对外暴露出来,我们只是提供了一个对外的方法。(至于你们会想为什么要这么设计呀,那你们思考一下你们为什么要穿衣服出门哟!)
代码实现:
想必现在你们学到这里也会知道java中的四种访问修饰符了吧,如果不知道快去网上搜搜以下是代码实现。
public class Dog {
//狗的名字
private String name;
//狗的年龄
private int age;
//对外提供获取狗的名字的方法
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;
}
}
代码解读:
以上代码就是一个简单的封装的实现,我们定义了一个狗的类,狗的类中有他的名字,和年龄,这个字段是用private修饰的字段,就是对外不进行暴露,然后呢我们提供了一个对外的方法就是public修饰的方法,然后外面的就可以通过我这个方法来获取到我的名字和年龄。
好处:
保证了数据的安全合理性。如果我们不进行封装,那么我们只需要调用这个字段名字(a.name=100)就可以对其进行修改,但是如果我们进行了封装我们就可以在对外提供的方法中进行校验,如果他符合我们的要求我们才能让他对我们的字段进行修改。
继承:
举一个例子:假如你的父亲生下了你,你有和你父亲一样的身体形态,你继承了你父亲的眼睛,鼻子,嘴等一些基础的形态,但是你始终和他不是一个人,你会有你自己的说话方式,语气形态等。简单点说就是继承就是继承了父类的所有东西,你可以在进行扩展你自己的东西。提高了我们代码的复用能力。
代码实现:
父类:
public class Animal {
private String name;
private int age;
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;
}
}
子类:(在这里我说一下extends(可以继承类)和 implements(可以继承接口) 都是继承的关键字)
public class Cat extends Animal{
private String eat;
public String getEat() {
return eat;
}
public void setEat(String eat) {
this.eat = eat;
}
}
测试类:
public class Test {
public static void main(String[] args) {
Cat cat = new Cat();
cat.setName("小花猫");
cat.setAge(2);
cat.setEat("fish");
}
}
代码解读:
在这个测试类中我们可以看到,我们可以调用父类的setName方法和setAge方法,也可以调用我们子类的setEat方法
好处:
继承的好处就是解决了我们代码的复用能力,如果我们的两个类有重多个重复的代码,可以使用继承的关系把公共的代码提升为一个父类,然后子类只需要继承这个父类就可以了。
多态!!!:
多态在我们的面向对象的三大特性中是最重要的一部分,是建立在封装,和继承之上,多态的大致意思就是对于方法和对象有多种形态。下面我们来仔细解读
方法的多态:
我们现在想方法的多种形态,什么意思呢,比如我们重载或者重写,就是方法多态的体现形式。
重载:
public class Test {
public static void main(String[] args) {
Test test = new Test();
test.test();
test.test("lisa");
test.test("jack", 20);
}
public void test(){
System.out.println("我是mike");
}
public void test(String name){
System.out.println("我是" + name);
}
public void test(String name, int age){
System.out.println("我是" + name + "我的年龄是" + age);
}
}
运行结果:
可以看到我们同样调用的都是test方法,只是我们传递的参数不同,就会有不同的表现形式。
重写:
父类:
public class Person {
public void eat(){
System.out.println("我是person我在吃饭");
}
}
子类:
public class Student extends Person{
@Override
public void eat() {
System.out.println("我是学生我在吃");
}
}
public class teacher extends Person{
@Override
public void eat() {
System.out.println("我是老师我在吃");
}
}
学生和老师类继承person类重写person中的eat方法。
测试类:
public class Test {
public static void main(String[] args) {
Student student = new Student();
teacher teacher = new teacher();
student.eat();
teacher.eat();
}
}
结果:
student类和teacher类都重写了父类的方法所以说我们同样都是调用eat方法但是结果是不同的。
对象的多态(核心,重点,难点):
对象的多态是我们的重点也是难点学习他之前我们首先要了解一个知识点就是什么是编译类型和什么是运行类型。
看如下图:
可以这个理解为 =号左面的是编译类型,等号右边的是运行类型。那这两个具体是什么意思呢?
编译类型:
我们的代码在运行的时候会先经过编译,编译成字节码文件,然后在JVM虚拟机上进行运行,编译类型是在编写代码时使用的类型,用于编译时的类型检查。
运行类型:
运行类型是程序运行时变量实际引用的对象的类型。可以这么理解我们真正在虚拟机上执行的是运行类型,所以我们也要关注的是=号右面的运行类型,这个是最重要的,因为由于多态性,编译类型和运行类型可以不一致。
向上转型:
了解编译类型和运行类型,那么我们来聊聊向上转型,(父类的引用指向子类的对象)这么说你肯定不懂也很抽象。下面我们来说说。
引出一个问题:假如我们有一个主人喂食的方法feed(),主人要给小猫喂食,主人要给小狗喂食那么代码是不是应该这么写如下:
public void feed(Cat cat){
System.out.println("主人给" + cat.getName() + "喂食" );
}
public void feed(Dog dog){
System.out.println("主人给" + dog.getName() + "喂食");
}
我们用了方法重载的多态性,实现了主人喂食的方法,但是你觉得如果有上百种动物,我难道要一个代码一个代码写feed方法吗?我们怎么能一个代码就解决这个问题呢?这就用我们对象的多态性就能解决啦这就是向上转型!!!
比如如图:
前提:必须是有继承关系,并且编译类型是运行类型的父类。这个就是典型的向上转型student是person的子类,那么我们以动物吃东西案例来体现。
代码实现:(动物吃东西案例)
父类:
public class Animal {
public void eat(){
System.out.println("我是动物我不知道吃啥");
}
}
子类:
public class Cat extends Animal{
public void eat(){
System.out.println("我是小猫我吃鱼");
}
}
public class Dog extends Animal{
public void eat(){
System.out.println("我是小狗我吃骨头");
}
}
测试类:
public class Test {
public static void main(String[] args) {
//向上转型
Animal cat = new Cat();
cat.eat();
//向上转型
Animal dog = new Dog();
dog.eat();
}
}
执行结果:
那么这是为什么会出现这个结果呢,那么就要看看上面我说的,JVM只认运行类型,所以说我们创建的是 =号右面的实例,所以我们就能调用对应实例的方法了 cat实例调用cat的eat方法,dog实例调用dog的eat方法。(这里有一点注意,如果cat或者dog没有eat方法,会调用父类的eat方法)
向上转型就可以解决我们上面喂食的问题了请看大屏幕。
/**
* 主人类
*/
public class Master {
public void feed(Animal animal){
animal.eat();
}
}
public class Test {
public static void main(String[] args) {
Master master = new Master();
//向上转型
Animal cat = new Cat();
master.feed(cat);
//向上转型
Animal dog = new Dog();
master.feed(dog);
}
}
解决:看多么神奇我只需要写一个feed方法,方法中的参数是父类的引用,我就可以实现喂食的方法了。
总结:
向上转型的条件是必须是继承关系,编译类型是运行类型的父类,并且如果想要调用方法的时候,子类必须重写父类的方法。
流程如下:
假如你要调用eat方法,首先会先看编译类型,编译类型是Animal,就会找到eat方法,然后编译通过,开始运行,但是运行类型是他的子类,然后在子类找是否有eat方法,如果没有在调用父类的eat方法。如果有直接返回,有人会问为什么要重写,在编译的时候他先找的是父类有没有eat方法,你父类没有你都编译不通过,你怎么运行,道理是不是这么个道理吧。那么有人又会问那我怎么可以调用到子类自己的方法呢那么你就用向下转型呗。
论证一下上面我说的话
子类:
public class Cat extends Animal{
public void eat(){
System.out.println("我是小猫我吃鱼");
}
public void see(){
System.out.println("我是小猫我在看");
}
}
测试类:
向下转型:
向下转型就是把编译类型转成我的子类,前提是必须是我之前指向的父类
这回就可以调用我的see方法了。