java面向对象

面向对象的特征:

1.封装
2.继承
3.多态

封装

封装表现:

1.方法就是一个最基本封装体。
2.类其实也是一个封装体。

封装的好处:

1.提高了代码的复用性。
2.隐藏了实现细节,还要对外提供可以访问的方式。便于调用者的使用。这是核心之一,也可以理解为就是封装的概念。
3.提高了安全性。

举例:就如同一台电脑主机,你无法看见其中的内部构造,但你能使用其中的内容,如显卡,集成器一 类的,如果直接交给你触碰,很容易直接坏掉,所有会用外壳包裹,以防毁坏,封装就是这类的思想,只给你具体的使用,但不给你展现,可以随时调用

修饰符引入:

public class Test {
    public static void main(String[] args) {
        Cat cat=new Cat();
        cat.age=5;
        cat.name="猫";
    }
}
class Cat{
    String name;
    int age;
}

从以上代码看到,定义了一个Cat类并在Test里面调用并赋值,但是,注意,封装要的是提供访问方式来使用,而不是让人随意直接修改,倘若能随意修改的话,比如机箱的话,一通修改,直接报废了,因此需要的是让专业的人(程序员)来访问修改,不让所有人随意使用,因此引入了修饰符

private

private关键字特点
是一个权限修饰符
可以修饰成员变量和成员方法
被其修饰的成员只能在本类中被访问

用private修饰Cat的成员属性后:

public class Test {
    public static void main(String[] args) {
        Cat cat=new Cat();
        cat.age =5;
        cat.name="猫";
    }
}
class Cat{
    private String name;
    private int age;
}
//此时的age和name会报错

访问方法

那么此时该如何才能不报错呢?
提供一个访问渠道就可以了,就像机箱会有插口一样,我们也提供给访问的方法

一般对成员属性的访问动作:赋值(设置 set),取值(获取 get),因此对私有的变量访问的方式可以提供对应的 setXxx或者getXxx的方法
IDEA可以用快捷键 Alt + insert选择生成get和set方法快速生成

public class Test {
    public static void main(String[] args) {
        Cat cat = new Cat();
//        cat.age=5;
//        cat.name="猫";
        cat.setAge(5);
        cat.setName("猫");
        //调用set方法设置具体
        System.out.println("年龄是::" + cat.getAge() + "名字是:" + cat.getName());
        //调用get方法得到

    }
}

class Cat {
    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;
    }
    //使用this防止变量冲突,也使得程序员看得懂,简单理解为此时的这个变量是这个
}

继承

这世界上人都有共同性,比如吃饭睡觉,如果每次创建类时如果都重复写一遍就很麻烦,那么就需要想个办法解决这些问题
而人都是继承来的,比如子与父,父亲会吃饭,会呼吸,那么自父母而来的孩子,也会吃饭睡觉,父亲或许会打球,如果孩子也会,那么就认为继承了父亲,因此便产生了使用继承的概念
通过对多个类中存在相同属性和行为进行抽取,将重复代码抽取到单独的类中,那么多个类无需再定义这些属性和行为,只要继承那个类即可。

Java中的继承是指在一个现有类的基础上去构建一个新的类,构建出来的新类被称为子类,现有的类被称为父类

继承格式

通过extends关键字可以实现类与类的继承
class 子类名 extends 父类名 {}

继承的好处:

  1. 提高了代码的复用性
  2. 提高了代码的维护性
  3. 让类与类之间产生了关系,这是多态的前提

演示:

public class Test {
    public static void main(String[] args) {
    	//创建子类对象
    	//注意,全是子类,并没有调用过父类
    	//全部调用父类方法
        Zi zi = new Zi();
        zi.setBreathe("能呼吸");
        zi.setEat("能吃");
        zi.play();
        System.out.println(zi.getBreathe()+"   "+zi.getEat());
    }
}

class Fu {
    String eat;
    String breathe;
    //定义各自都有的

    public void play() {
        System.out.println("打篮球");
    }
    //定义两人共有爱好

    //创建方法调用
    public String getEat() {
        return eat;
    }

    public void setEat(String eat) {
        this.eat = eat;
    }

    public String getBreathe() {
        return breathe;
    }

    public void setBreathe(String breathe) {
        this.breathe = breathe;
    }
   
}

class Zi extends Fu {
	//此时子类中没有任何东西
}

继承注意事项:

1.Java只支持单继承,不支持多继承。(一个儿子只能有一个亲生父亲)
2.子类只能继承父类所有非私有的成员(成员方法和成员变量,你父亲愿意给你你才有)
3.子类不能继承父类的构造方法,但是可以通过super关键字去访问父类构造方法。

构造方法

什么是构造方法?

从字面上理解就是 创建对象 时要 执行 的方法。那么只要在 new 对象时,知道其执行的构造方法是什么,就可以在执行这个方法的时候给对象进行 属性赋值 。
构造方法也叫构造器,主要作用是创建对象,给对象中的成员进行初始化

构造方法格式

修饰符 构造方法名(参数列表){
}

构造方法特点

1.构造方法 没有返回值类型 以及 返回值 (特殊的方法,连void都没有)。
2.不需要写返回值。因为它是为构建对象的,对象创建完,方法就执行结束(只用这一次)
3.构造方法名称必须和 类名保持一致。

同样,IDEA可以用 Alt + insert 选择构造函数生成方法

public class Test {
    public static void main(String[] args) {
    	//使用方法
    	//可以直接赋值,省去调用set方法
        Fu fu=new Fu("吃饭","呼吸");
        System.out.println(fu.getBreathe()+"   "+fu.getEat());
    }
}

class Fu {
    String eat;
    String breathe;
    
    public void play() {
        System.out.println("打篮球");
    }

    //定义无参方法(默认构造)
    public Fu() {

    }

    //定义带参方法
    //通过构造方法可以为成员变量赋值,也就是对成员变量进行了初始化
    public Fu(String eat, String breathe) {
        this.eat = eat;
        this.breathe = breathe;
    }

    public String getEat() {
        return eat;
    }

    public void setEat(String eat) {
        this.eat = eat;
    }

    public String getBreathe() {
        return breathe;
    }

    public void setBreathe(String breathe) {
        this.breathe = breathe;
    }
}

注意:上述代码为何要有个无参方法:

当在编译Java文件时,编译器会自动给class文件中添加 默认的构造方法。如果在描述类时,我们显式指定了构造方法,那么,当在编译Java源文件时,编译器就不会再给 class 文件中添加 默认构造 方法。

class Fu {
	//如果没有显式指定构造方法,编译会在编译时自动添加默认的构造方法
	//public Fu(){}  //空参数的默认构造方法
}

建议无论如何都把无参方法写上,详情在后–>继承中的构造方法注意事项

方法重载

当我们只想用一个属性时就会报错
Fu fu=new Fu(“吃饭”);

public class Test {
    public static void main(String[] args) {
    	//此时括号报错,因为缺少了"呼吸"
        Fu fu=new Fu("吃饭");
        System.out.println(fu.getEat());
    }
}

因此可以使用方法的重载

public class Test {
    public static void main(String[] args) {
        Fu fu=new Fu("吃饭","呼吸");
        System.out.println(fu.getBreathe()+"   "+fu.getEat());
        Fu fu1=new Fu("吃饭");
        System.out.println(fu1.getEat());
    }
}

class Fu {
    String eat;
    String breathe;

    public void play() {
        System.out.println("打篮球");
    }

    //定义无参方法
    public Fu() {

    }

    //定义带参方法
    public Fu(String eat, String breathe) {
        this.eat = eat;
        this.breathe = breathe;
    }

    //定义带一个参的方法
    public Fu(String eat) {
        this.eat = eat;
    }

    public String getEat() {
        return eat;
    }

    public void setEat(String eat) {
        this.eat = eat;
    }

    public String getBreathe() {
        return breathe;
    }

    public void setBreathe(String breathe) {
        this.breathe = breathe;
    }
}

此时就可以用单个的eat属性了

super / this

在继承中,如果子类和父类变量重名了,那么子类想访问父类变量,此时就需要用super关键字
当局部变量和成员变量的名字相同的时,使用this关键字,解决局部变量隐藏(冲突)了成员变量的问题

super 使用
super.成员变量 调用父类的成员变量
super.(…) 调用父类构造方法
super.成员方法 调用父类成员方法

相识知识点:this关键字
this.成员变量 调用本类的成员变量
this(…) 调用本类的构造方法
this.成员方法 调用本类的成员方法

this和super的区别
this代表的是本类对象的引用
super代表的是父类存储空间的标识(可以理解成父类的引用,可以操作父类的成员)

继承中成员变量的关系

子类中的成员变量和父类中的成员变量名称不一样,可以直接使用

子类中变量名与父类中成员变量名一样:
变量寻找方式,遵循就近原则
1.先在局部位置找变量
2.如果局部找不到,那么就到当前类成员变量找
3.如果当前类找不到,那么到父类里面找
4.再找不到,就报错

注意:当子类覆盖父类的成员变量时,每个类使用成员都相当于在前面加了 一个this指针。

public class Test {
    public static void main(String[] args) {
		//创建子类对象
		Zi z = new Zi();
		z.show();
	}
}

class Fu {
	public int num = 20;
	public void showFu(){
	    System.out.println(num);
	}
}

class Zi extends Fu{
	public int num2 = 40;
    //第二次演示 跟父类同名的变量
	public int num = 100;
	public void show(){
	  //第三次演示  定义局部变量 num 
	   int num = 200;
	   System.out.println(num2);//子类的
	   //System.out.println(num);//第一次演示是父类的
       //System.out.println(num);//第二次演示 100
	   System.out.println(num);
	   showFu();
	}
}

结果:

40
200
20

继承中构造方法的关系

子类中所有的构造方法默认都会访问父类中空参数的构造方法

为什么呢?
因为子类会继承父类中的数据,可能还会使用父类的数据。
所以,子类初始化之前,一定要先完成父类数据的初始化。
其实:
每一个构造方法的第一条语句默认都是:super()
如果这个类没有去继承其他的类,默认继承Object类,每个类都使用 Object 作为超类(最大父类)

public class Test {
    public static void main(String[] args) {
        //创建对象
        Zi zi = new Zi();
        System.out.println("------------------------------");
        Zi zi1 = new Zi("子");
    }
}

class Fu {
    public Fu() {
        System.out.println("我是Fu的无参构造!");
    }

    public Fu(String name) {
        //super();
        System.out.println("我是Fu的带参构造!");
    }
}

class Zi extends Fu {
    public Zi() {
        //super();
        System.out.println("我是Zi的无参构造!");
    }

    public Zi(String name) {
        //super();
        //访问父类的带参构造
        super(name);
        System.out.println("我是Zi的有参构造!");
    }
}

结果:

我是Fu的无参构造!
我是Zi的无参构造!
------------------------------
我是Fu的带参构造!
我是Zi的有参构造!

继承中构造方法的注意事项

父类没有无参构造方法,子类怎么办?
在父类中添加一个无参的构造方法
子类通过super去显示调用父类其他的带参的构造方法
子类通过this去调用本类的其他构造方法
子类其他构造必须首先访问父类构造

public class Test {
    public static void main(String[] args) {
        //创建对象
        //Zi z = new Zi();
        Zi z2 = new Zi("子");
    }
}

class Fu {
    /*
    public Fu(){
       System.out.println("父类无参构造!");
    }
    */
    public Fu(String name) {
        System.out.println("父类带参构造!");
    }
}

class Zi extends Fu {
    public Zi() {
        //super();
        super("张三");
        System.out.println("子类无参构造!");
    }

    public Zi(String name) {
        //super();
        //super("aa");
        this();
        System.out.println("子类带参构造!");
    }
}

结果:

父类带参构造!
子类无参构造!
子类带参构造!

继承中成员方法关系

当子类的方法名和父类的方法名不一样的时候
当子类的方法名和父类的方法名一样的时候

通过子类调用方法:
先查找子类中有没有该方法,如果有就使用
再看父类中有没有该方法,有就使用
如果没有就报错

public class Test {
    public static void main(String[] args) {
        //创建一个子类对象
        Zi z = new Zi();
        z.show();
        z.method();
        //z.function();// z.function();找不到
    }
}

class Fu {
    public void show() {
        System.out.println("show Fu!");
    }
}

class Zi extends Fu {
    public void method() {
        System.out.println("method Zi!");
    }

    public void show() {
        System.out.println("show Zi!");
    }
}

结果:

show Zi!
method Zi!

方法重写

当子类继承了父类中的方法时,但又要更改一部分,例如手机,所有手机都能打电话,新的手机继承以前手机的功能,但是新的手机还可以在打电话时视频等,因此就可以使用方法重写来既继承打电话功能,又可以添加新的视频等功能

public class Test {
    public static void main(String[] args) {
        newPhone np=new newPhone();
        np.call();
    }
}

class Phone{
    public void call(){
        System.out.println("打电话");
    }
}
class newPhone extends Phone{
    @Override
    public void call() {
        super.call();//这句话体现了继承的魅力
        System.out.println("开启视频功能");
    }
}

结果:

打电话
开启视频功能

方法重写注意事项

父类中私有方法不能被重写
因为父类私有方法子类根本就无法继承(你父亲给你看你才能看)
子类重写父类方法时,访问权限不能更低(你父亲有资格知道你的,你没资格比他知道的多)
最好就一致
父类静态方法,子类也必须通过静态方法进行重写
其实这个算不上方法重写,但是现象确实如此,至于为什么算不上方法重写,多态中会讲解
子类重写父类方法的时候,最好声明一模一样。

多态

多态概述

现实事物经常会体现出多种形态,如学生,学生是人的一种,则一个具体的同学张三既是学生也是人,即出现两种形态。

Java作为面向对象的语言,同样可以描述一个事物的多种形态。如Student类继承了Person类,一个Student的对象便既是Student,又是Person。

Java中多态的代码体现在一个子类对象(实现类对象)既可以给这个子类(实现类对象)引用变量赋值,又可以给这个子类的父类类型变量赋值。

如Student类可以为Person类的子类。那么一个Student对象既可以赋值给一个Student类型的引用,也可以赋值给一个Person类型的引用。

最终多态体现为父类引用变量可以指向子类对象。
多态的前提是必须有子父类关系或者类实现接口关系,否则无法完成多态。
在使用多态后的父类引用变量调用方法时,会调用子类重写后的方法

多态的定义与使用格式

多态的定义格式:就是父类的引用变量指向子类对象
父类类型 变量名 = new 子类类型();
变量名.方法名();

普通类(继承关系)多态定义的格式

父类 变量名 = new 子类();
如: class Fu {}
class Zi extends Fu {}
//类的多态使用
Fu f = new Zi();

抽象类多态定义的格式

抽象类 变量名 = new 抽象类子类();
如:
abstract class Fu {
     public abstract void method();
}
class Zi extends Fu {
     public void method(){
System.out.println(“重写父类抽象方法”);
    }
}
//类的多态使用
Fu fu= new Zi();

接口多态定义的格式

接口 变量名 = new 接口实现类();
如:
interface Fu {
     public abstract void method();
}
class Zi implements Fu {
     public void method(){
          System.out.println(“重写接口抽象方法”);
     }
}
//接口的多态使用
Fu fu = new Zi();

多态中成员变量

public class Test {
    public static void main(String[] args) {
        Fu f = new Zi();
        System.out.println(f.num);
        Zi z = new Zi();
        System.out.println(z.num);
    }
}

class Fu {
    int num = 4;
}

class Zi extends Fu {
    int num = 5;
}

结果:

4
5

当子父类中出现同名的成员变量时,多态调用该变量时:
编译时期:参考的是引用型变量所属的类中是否有被调用的成员变量。如果没有,编译错误
运行时期:也是调用引用型变量所属的类中的成员变量。
简单记:编译和运行都参考等号的左边。编译运行看左边。

Java成员变量没有重写的功能,多态调用时先确定变量的类型,如
//此时f类型是Fu(门面类型),只能取到Fu中的值
//(父类不知道子类有什么属性,但是子类是知道父类的属性的)
//可以理解为,父子两人的回答,往往只能是父的更容易让人信服
Fu f = new Zi();
f.num;

多态中成员方法

public class Test {
    public static void main(String[] args) {
        Fu f = new Zi();
        f.show();
    }
}

class Fu {
    void show() {
        System.out.println("Fu show num");
    }
}

class Zi extends Fu {
    void show() {
        System.out.println("Zi show num");
    }
}

结果:

Zi show num

编译时期:参考引用变量所属的类,如果类中没有调用的方法,编译失败。
运行时期:参考引用变量所指的对象所属的类,并运行对象所属类中的成员方法。
简而言之:编译看左边,运行看右边。(因为方法可以重写)

Java成员方法是有重写的功能,多态调用时先确定变量的类型,如
//此时f类型是Fu(门面类型),但是实际的类型是Zi
//可以理解为,时代变了!大人,你的方法过时了!年轻人更改了最新最好用的,所以听我的!
Fu f = new Zi();
f.show();//f的门面类型是Fu,但是实际类型是Zi,所以调用的是重写后的方法

多态的转型

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

向上转型:当有子类对象赋值给一个父类引用时,便是向上转型,多态本身就是向上转型的过程。
使用格式:
父类类型 变量名 = new 子类类型();
如:Fu f = new Zi();

向下转型:一个已经向上转型的子类对象可以使用强制类型转换的格式,将父类引用类型转为子类引用类型,这个过程是向下转型。
如果是直接创建父类对象,是无法向下转型的!
使用格式:
子类类型 变量名 = (子类类型) 父类类型的变量;
如:Zi zi = (Zi) f; //变量p 实际上指向Student对象

转型举例:

描述土豪
土豪拥有吃饭功能
描述富二代
富二代拥有玩游戏功能
都拥有吃饭的方法
某一天土豪生病了,富二代代替土豪去签合同
签名:签的是土豪的名字
吃饭:富二代喜欢吃啥就点啥
回家后换回身份:玩游戏

public class Test {
    public static void main(String[] args) {
        // 土豪生病了,但是有合约要签
        Fu f = new Zi();// 富二代代替他爹去签合约 向上转型(此时可以看作是土豪)
        System.out.println(f.name);// 土豪的名字(使用的是变量)
        f.eat();// 签完合同去吃饭,这里表象是Fu,但是实际是Zi(使用的是方法)

        Zi z = (Zi) f;// 回家,换回自己的身份 向下转型(转回富二代)
        z.playGames();
    }
}

class Fu {
    int age = 50;
    String name = "土豪";

    public void eat() {
        System.out.println("清粥白菜...");
    }
}

class Zi extends Fu {
    String name = "富二代 ";
    int age = 25;

    public void eat() {
        System.out.println("大鱼大肉!");
    }

    public void playGames() {
        System.out.println("玩游戏!");
    }
}

结果:

土豪
大鱼大肉!
玩游戏!

多态好处与弊端

好处:
当父类的引用指向子类对象时,就发生了向上转型,即把子类类型对象转成了父类类型。向上转型的好处是隐藏了子类类型,提高了代码的扩展性。

弊端:
但向上转型也有弊端,只能使用父类共性的内容,而无法使用子类特有功能,功能有限制。如果想要使用子类内容,那么必须强转为子类类型

案例:
f.playGames ();

public class Test {
    public static void main(String[] args) {
//        // 土豪生病了,但是有合约要签
//        Fu f = new Zi();// 富二代代替他爹去签合约 向上转型(此时可以看作是土豪)
//        System.out.println(f.name);// 土豪的名字(使用的是变量)
//        f.eat();// 签完合同去吃饭,这里表象是Fu,但是实际是Zi(使用的是方法)
//        Zi z = (Zi) f;// 回家,换回自己的身份 向下转型(转回富二代)
//        z.playGames();

        //富二代冒充土豪
        Fu f = new Zi();
        //富二代用土豪身份玩游戏
        f.playGames ();//报错,此时的富二代是土豪,土豪不喜欢玩游戏,因此不能玩游戏
        //富二代换回自己身份
        Zi z = (Zi) f;
        z.playGames();//可以玩了
    }
}

class Fu {
    int age = 50;
    String name = "土豪";

    public void eat() {
        System.out.println("清粥白菜...");
    }
}

class Zi extends Fu {
    String name = "富二代 ";
    int age = 25;

    public void eat() {
        System.out.println("大鱼大肉!");
    }

    public void playGames() {
        System.out.println("玩游戏!");
    }
}

什么时候使用向上转型:
当不需要面对子类类型时,通过提高扩展性,或者使用父类的功能就能完成相应的操作,这时就可以使用向上转型。
如:Fu f = new Zi();
        f.eat();

什么时候使用向下转型
当要使用子类特有功能时,就需要使用向下转型。
如:Zi z = (Zi) f; //向下转型
        z.playGames();//调用富二代的玩游戏方法
向下转型
好处是:可以使用子类特有功能。
弊端是:需要面对具体的子类对象;在向下转型时容易发生ClassCastException类型转换异常。在转换之前必须做类型判断。
如:if(!f instanceof Zi){…}

instanceof关键字

我们可以通过instanceof关键字来判断某个对象是否属于某种数据类型。如学生的对象属于学生类,学生的对象也属于人类。
使用格式:
boolean b = 对象 instanceof 数据类型;

Person p1 = new Student(); // 前提条件,学生类已经继承了人类(Student extends Person)
boolean flag = p1 instanceof Student; //flag结果为true
boolean flag2 = p2 instanceof Teacher; //flag结果为false

总结

学习到这里,面向对象的三大特征学习完了。
总结下封装、继承、多态的作用:
封装:把对象的属性与方法的实现细节隐藏,仅对外提供一些公共的访问方式
继承:子类会自动拥有父类所有可继承的属性和方法。
多态:配合继承与方法重写提高了代码的复用性与扩展性;如果没有方法重写,则多态同样没有意义。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值