Java学习之面向对象

发布时间:2023年12月17日

一、面向对象

1、引入面向对象

方法中封装的是具体实现某一功能的代码,而通过书写一个拥有多个特定方法的类,来存放的就是一个又一个的方法

方法都存放在类里面,当需要使用的时候,不用去找具体的方法,而是先找这个类,那么就自然而然可以找到其中封装的方法,而调用这些方法的时候,我们需要获取这个类的实例,才能进行调用,那么这个实例,就是这个类的对象

方法 :是用于封装功能代码,类用于封装具有某一类相似特性的功能。

面对需求 : 先找Java有没有提供相应的类可以完成此类需求。若没有就自己写个类将功能封装在其中。

2、什么是面向对象

当不再面对具体的每一个方法时,发现操作也变的简单了很多。而封装具体功能的这个类,才是我们需要面对的。而基于这个封装了具体功能的类,一般情况下,在Java中需要通过创建这个类的实体来使用。

这个实体称之为对象。在开发中,我们是在不断的找封装不同功能的类。基于这些类创建其对象,使用这些对象完成相应的操作。

通过上面的讲解和分析得出:面向对象是基于面向过程对象是将功能进行了封装。只要找到了具体的类,创建出对象,就可以调用其中的具体功能。面向对象也是用来解决问题的一种思维模式。

在以后开发中,先找对象,调用对象中的具体功能。如果真的没有能够完成需求的对象,这时就自己创建对象,并将所需的功能定义到对象中,方便以后使用。

3、面向对象和过程的差异

通过上述的分析和举例对面向过程和面向对象做出总结:

  1. 面向对象是一种更符合人们思考习惯的思想。
  2. 面向过程中更多的体现的是执行者,面向对象中更多的体现是指挥者。指挥对象做事情。
  3. 面向对象将复杂的问题简单化。

千言万语总结出一句话:万事万物,皆对象

4、举例

大象冰箱举例:一个把大象装进冰箱的需求

在针对具体的需求,可以使用名词提炼法进行分析,寻找具体的对象。

  • 把大象装冰箱里
  • 大象、冰箱

实施的步骤:

  1. 打开冰箱门
  2. 将大象装进去
  3. 关闭冰箱门

分析发现打开、装、关闭都是冰箱的功能。即冰箱对象具备如下功能:

  • 冰箱打开
  • 冰箱存储
  • 冰箱关闭
// 创建冰箱对象
冰箱 bx = new 冰箱(); 
// 调用冰箱的功能:对象.功能();
bx.打开();
bx.存储(new 大象());
bx.关闭();

总结:

  1. 先提炼问题领域中的对象。
  2. 对象进行描述,其实就是在明确对象中应该具备的属性和功能。
  3. 通过?new?的方法就可以创建该事物的具体对象。
  4. 通过该对象调用它以后的功能。使用?.?访问属性和调用方法()

面向对象程序设计(OOP)就是使用对象进行程序设计。对象(object)代表现实世界中可以明确标识的一个实体。例如:一个学生、一张桌子、一个圆、一个按钮甚至一笔贷款都可以当做是一个对象。每个对象都有自己独特的标识、状态和行为。
对象的状态由属性来决定; 对象的行为由方法定义。
使用一个通用类来定义同一类型的对象,对象就是类的实例,就像现实中你的“对象”一样,对象是个类型,你得实例化她,就是你要有个有血有肉的真正对象,找对象就像实例化对象。
创建实例的过程称之为:实例化
例:

public class Circle {

    public final double PI = Math.PI;
    // 半径,默认值为1。
    double radius = 1;
    // 构造方法1
    public Circle(double radius) {
        this.radius = radius;
    }
    // 构造方法2
    public Circle() {
    }
        this.radius = radius;
    public double getArea(){
        return PI*radius*radius;
    }
    // 圆的周长
    public double getPerimeter(){
        return 2*PI*radius;
    }
}
·················································································
// 创建三个圆形
Circle circle1 = new Circle();
Circle circle2 = new Circle();
Circle circle3 = new Circle();

编写一个类Circle,创建三个Circle的实例化对象(new.类名)。

二、对象

1、对象概述

认识了什么事面向对象,那么对于Java学习中永远逃不开的名词——对象,我们需要做额外的更详细的介绍!

1.1、对象在代码中的体现

在分析现实生活中的事物时发现,这些事物都有其具体的特点和功能,这些特点和功能就组成了这个特殊的事物。

描述小汽车,分析其具有的属性和行为。

  • 事物的特点(属性):颜色,轮胎个数。
  • 事物的行为(方法):运行。

可以简单理解:属性就是数值,其实就是变量;行为就是功能,就是函数。

类的真正意义就是在描述事物。属性行为统称为事物中的成员

事物的成员分为两种:成员属性和成员行为。

成员属性:在代码中的体现就是成员变量

成员行为:在代码中的体现就是成员函数(方法)
例如:

class Car{
	String color;// 颜色
	int number;// 数量
    // 运行方法!!!
	void run(){
		System.out.println(color+"::"+number);
	}
}
class CarDemo{
	public static void main(String[] args){	
        //测试:Car类中的run方法。
		//1,创建Car的对象。给对象起个名字。
		Car c = new Car();//c是类类型的变量。c指向了一个具体的Car类型的对象。
		//2,通过已有的对象调用该对象的功能。格式:对象.对象成员;
		//3,可以该对象的属性赋值。
		c.color = "red";
		c.number = 4;
		c.run();
	}
}

1.2、对象内存图解

在这里插入图片描述
堆中开辟内存,有默认初始值,String类型默认为null(空),int类型默认为0.

1.3、类与对象的区别

类是用于描述现实事物的,它将现实事物进行抽象化,模板化描述。将事物的特点(属性)和行为封装在其中。比如小汽车的图纸,图纸就是小汽车的模版。图纸上画着小汽车的各种特点和功能要求。

对象是现实生活中存在的具体的实例、个体。即生活中看到每一个事物,以及我们想象中的任务抽象的概念,都是某一类事物的实例和个体。而这些个体都属于某一类事物,即这些个体都是某一类事物中的具体的实例。比如,小汽车就是一类事物,而小汽车又是基于小汽车图纸制造出来的真实个体。因此我们生活中的每一个实物都可以理解为是某一类事物的中的一个个体。创建对象,通过对象就可以调用具体的属性和行为。

1.4、局部变量和成员变量区别

  • 定义的位置不同
    成员变量定义在中。
    局部变量定义在方法中或者语句里面。
  • 在内存中的位置不同
    成员变量存储在内存的对象中。
    局部变量存储在内存的方法中。
  • 声明周期不同
    成员变量随着对象的出现而出现在堆中,随着对象的消失而从堆中消失。
    局部变量随着方法的运行而出现在栈中,随着方法的弹栈而消失。
  • 初始化不同
    成员变量因为在堆内存中,所以有默认的初始化值
    局部变量没有默认的初始化值,必须手动的给其赋值才可以使用。

2、对象的使用

2.1、构造函数

构造函数: 从字面上理解即为构建创造时用的函数,即就是对象创建时要执行的函数

既然是对象创建时要执行的函数,那么只要在new对象时,知道其执行的构造函数是什么,就可以在执行这个函数的时候给对象进行属性赋

构造函数的格式:
修饰符 构造函数名(参数列表){
}

构造函数的特点:

  1. 构造函数没有返回值类型。也不需要写返回值。因为其是为构建对象的,对象创建完,函数就执行结束。
  2. 构造函数名称必须和类名保持一致
    代码:
//构造函数的代码体现:
class  Person{
	//Person的成员属性age和name
	private int age;
	private String name;
	//Person的构造函数,拥有参数列表
	Person(int a , String nm){
		//接受到创建对象时传递进来的值,将值赋给成员属性
		age = a;
		name = nm;
	}
}

?构造函数是专门用来创建对象的,也就是在new对象时要调用构造函数。
在创建对象时,会调用与参数列表对应的构造函数。

例如:

class  Person{
	//Person的成员属性age和name
	private int age;
	private String name;
	//Person的构造函数,拥有参数列表
	Person(int a , String nm){
		//接受到创建对象时传递进来的值,将值赋给成员属性
		age = a;
		name = nm;
	}
	public void speak(){
		System.out.println("name="+name+",age="+age);
	}
}
class PersonDemo{
	public static void main(String[] args){
		//创建Person对象
		//Person p = new Person();  
        // 定义了其他的构造方法 空构造就没有了Person()
		//上述代码编译报错,原因是在Person类中没有Person()这样的构造函数
		//创建Person对象,并明确对象的年龄和姓名
		Person p2 = new Person(23,"张三");
		p2.speak();
	}
}

描述事物时,并没有显示指定构造函数,当在编译Java文件时,编译器会自动给class文件中添加默认的构造函数。如果在描述事物时,显示指定了构造函数,当在编译Java源文件时,编译器就不会再给class文件中添加默认构造函数。

要不要写构造函数呢?一句话:当描述的事物在创建其对象时就要明确属性的值,这时就需要在定义类的时候书写带参数的构造函数。若创建对象时不需要明确具体的数据,这时可以不用书写构造函数(不书写也有默认的构造函数)。

默认构造函数由编译器添加。

构造函数的细节:

  1. 一个类中可以有多个构造函数,多个构造函数是以重载的形式存在的。
  2. 构造函数中也是有return语句的,用于结束初始化动作。
  3. 构造函数是可以被private修饰的,作用:其他程序无法创建该类的对象。

构造函数和一般函数区别:

  1. 构造函数在对象创建时就执行了,而且只执行一次。
  2. 一般函数是在对象创建后,需要使用时才被对象调用,并可以被多次调用。

构造函数之间的调用可以通过this关键字来完成。

class Person {
	// Person的成员属性
	private int age;
	private String name;
	//无参数的构造函数
	Person(){
	}
	//给姓名初始化的构造函数
	Person(String nm){
		name = nm;
	}
	//给姓名和年龄初始化的构造函数
	Person(String nm , int a){
		//由于已经存在给姓名进行初始化的构造函数 name = nm;因此只需要调用即可
		//调用其他构造函数,需要通过this关键字来调用
		this(nm);
		//给年龄初始化
		age = a;
	}
}

2.2、匿名对象

我们在使用对象的时候都会给对象创建引用,并且通过这个引用来使用创建的对象。看如下代码:

class Car{
	//描述属性。颜色,轮胎数。
	String color;
	int number;
	//描述行为。
	void run(){
		System.out.println(color+":"+number);
	}
}

class CarDemo{
    public static void main(String[] args){
         //创建Car.class类的对象。
		 Car c = new Car();
         //调用run方法
         c.run();
         //创建Car.class类的对象。
         Car c2 = new Car();
        //调用run方法
         c2.run();
     }
}

在main方法中,每次创建对象,只为了调用其run方法,而这时定义的c引用变量,几乎没有什么作用。而c等价于new Car();这时可以使用简化书写成new Car().run();

new Car();这行代码并没有给Car对象创建引用,即没有给对象起名字,这样的代码就称之为匿名对象。匿名对象可以简化上述代码。

?为了简化书写,会使用匿名对象。匿名对象的使用场景:当对象对方法进行调用时,而且只调用一次时,可以简化成匿名对象来书写。

匿名对象也可以作为参数传递:

class CarDemo{
    public static void main(String[] args){    
        Car c = new Car();
        show(c);    //简化成 show(new Car());//把匿名对象作为实际参数进行传递。
     }
    public static void show(Car cc)  // Car cc = c;  Car cc = new Car();
	{
		cc.color = "red";
		cc.number = 4;
		cc.run();
	}
}

三、对象三大特性

1、封装

1.1、封装的概念

举例,计算机的主机机箱:

一台电脑,它是由CPU、主板、显卡、内存、硬盘、电源等部件组长,其实我们将这些部件组装在一起就可以使用电脑了,但是发现这些部件都散落在外面,很容造成不安全因素,于是,使用机箱壳子,把这些部件都装在里面,并在机箱壳上留下一些插口等,若不留插口,大家想想会是什么情况。

总结:机箱其实就是隐藏了办卡设备的细节,对外提供了插口以及开关等访问内部细节的方式。

同理,在Java中,一个类中也可以封装自己的多个功能,然后通过方法对外提供访问方式,但不是所有方法都支持通过创建该类的对象进行调用,这样的方法我们需要使用一种修饰符来进行限定。

1.2、private修饰符

/**
 * 描述人。Person
 * 属性:年龄。
 * 行为:说话:说出自己的年龄
 */
public class Person{
    int age ;// 年龄
    String name;// 姓名

    // 展示
    public void show(){
        System.out.println("age="+age+",name"+name);
    }
}

class PersonDemo{
    public static void main(String[] args){
        //创建Person对象
        Person p = new Person();
        p.age = -20;  // 给Person对象赋值
        p.name = "张三";
        p.show(); //调用Person的show方法
    }
}

通过上述代码发现,虽然我们用Java代码把Person描述清楚了,但有个严重的问题,就是Person中的属性的行为可以任意访问和使用。这明显不符合实际需求。

可是怎么才能不让访问呢?需要使用一个Java中的关键字也是一个修饰符?private(私有,权限修饰符)。只要将Person的属性和行为私有起来,这样就无法直接访问。
如:

class Person 
{
	private int age ;
	private String name;

	public void show() 
	{
		System.out.println("age="+age+",name"+name);
	}
}

年龄已被私有,错误的值无法赋值,可是正确的值也赋值不了,这样还是不行,那肿么办呢?按照之前所学习的封装的原理,隐藏后,还需要提供访问方式。只要对外提供可以访问的方法,让其他程序访问这些方法。同时在方法中可以对数据进行验证。

一般对成员属性的访问动作:赋值(设置 set)取值(获取 get),因此对私有的变量访问的方式可以提供对应的 setXxx或者getXxx的方法。
代码:

class Person 
{
	//私有成员变量
	private int age ;
	private String name;
	//对外提供设置成员变量的方法
	public void setAge(int a)
	{
		//由于是设置成员变量的值,这里可以加入数据的验证
		if( a < 0 || a > 130 )
		{
			System.out.println(a+"不符合年龄的数据范围");
			return;
            //throw new RuntimeException("对不起,您的年龄数值 "+a+" 是非法的。");//异常!一旦出现,程序结束。需要修正代码。
		}
		age = a;
	}
	//对外提供访问成员变量的方法
	public void getAge()
	{
		return age;
	}
	//name同理
}

由于Person中的属性都被private了,外界无法直接访问属性,必须对外提供相应的set和get方法,如果想在创建的时候就明确其姓名和年龄或者其他属性,我们可以提供不同的构造函数。

?2、继承

继承主要依赖Java中接口和抽象类的概念,可异步我的上一篇博客:Java入门学习笔记二-CSDN博客

3、多态

实际上,java对象的三大特性,上述的两个特性可以说都是为了多态进行服务的。

3.1、多态的体现:

父类的引用或者接口的引用指向了自己的子类对象

Dog d = new Dog();//Dog对象的类型是Dog类型。
Animal a = new Dog();//Dog对象的类型右边是Dog类型,左边Animal类型。

多态的利弊

好处:提高了代码的扩展性。

弊端:通过父类引用操作子类对象时,只能使用父类中已有的方法,不能操作子类特有的方法。

多态的前提:

  1. 必须有关系:继承,实现。
  2. 通常都有重写操作。

3.2、 多态-转型

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

但向上转型也有弊端,只能使用父类共性的内容,而无法使用子类特有功能,功能有限制。

向上转型:

不需要面对子类类型时,通过提高扩展性,或者使用父类的功能就能完成相应的操作,这时就可以使用向上转型。

向下转型:
例如:

Animal a = new Dog(); //这里形成了多态
		a.eat();
Dog d = (Dog)a;//向下转型

当要使用子类特有功能时,就需要使用向下转型

好处:可以使用子类特有功能。

弊端:需要面对具体的子类对象;在向下转型时容易发生ClassCastException类型转换异常。在转换之前必须做类型判断。

3.3、举例练习

需求:

  • 阶段一:使用笔记本,笔记本有运行功能,需要笔记本对象来运行这个功能。
  • 阶段二:想使用一个鼠标,又有一个功能使用鼠标,并多了一个鼠标对象。
  • 阶段三:还想使用一个键盘,又要多一个使用键盘功能多一个对象。
  • 问题:每多一个功能就需要在笔记本对象中定义一个函数,程序扩展性极差。
//定义鼠标、键盘,笔记本三者之间应该遵守的规则
interface USB{
	void open();//开启功能
	void close();//关闭功能
}
//鼠标实现USB规则
class Mouse implements USB{
	public void open(){
		System.out.println("鼠标开启");
	}
	public void close(){
		System.out.println("鼠标关闭");
	}
}
//键盘实现USB规则
class KeyBoard implements USB{
	public void open(){
		System.out.println("键盘开启");
	}
	public void close(){
		System.out.println("键盘关闭");
	}	
}
// 定义笔记本
class NoteBook{
	//笔记本开启运行功能
	public void run(){
		System.out.println("笔记本运行");
	}
	//笔记本使用usb设备,这时当笔记本实体调用这个功能时,必须给其传递一个符合USB规则的USB设备
	public void useUSB(USB usb){
		//判断是否有USB设备
		if(usb != null){
			usb.open();
			usb.close();
		}
	}
	public void shutDown(){
		System.out.println("笔记本关闭");
	}
}
public class Test {
	public static void main(String[] args) {
		//创建笔记本实体对象
		NoteBook nb = new NoteBook();
		//创建鼠标实体对象
		Mouse m = new Mouse();
		//创建键盘实体对象
		KeyBoard kb = new KeyBoard();
		//笔记本开启
		nb.run();
		//笔记本使用鼠标
		nb.useUSB(m);
		//笔记本使用键盘
		nb.useUSB(kb);
		//笔记本关闭
		nb.shutDown();
	}
}

3.4、多态成员的特点

多态出现后会导致子父类中的成员变量有微弱的变化。如下代码所示:

class Fu{
	int num = 4;
}
class Zi extends Fu{
	int num = 5;
}
class Demo {
	public static void main(String[] args) {
		Fu f = new Zi();
		System.out.println(f.num);
		Zi z = new Zi();
		System.out.println(z.num);
	}
}
//4
//5

多态成员变量:

子父类中出现同名的成员变量时,多态调用该变量时:

编译时期:参考的是引用型变量所属的类中是否有被调用的成员变量。没有,编译失败。

运行时期:也是调用引用型变量所属的类中的成员变量。

简单记:编译和运行都参考等号的左边。编译运行看左边

多态成员函数:

编译时期:参考引用变量所属的类,如果没有类中没有该调用的函数,编译失败。

运行时期:参考引用变量所指的对象所属的类,并运行对象所属类中的成员函数。

简而言之:编译看左边,运行看右边

class Fu{
    int num = 4;
    void show(){
        System.out.println("Fu show num"+num);
    }
}
class Zi extends Fu{
    int num = 5;
    void show(){
        System.out.println("Zi show num"+num);
    }
}
class Demo{
    public static void main(String[] args){
        Fu f = new Zi();
        f.show();
    }
}

文章来源:https://blog.csdn.net/qq_45901741/article/details/135017022
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。