7.javase-面向对象

面向过程和面向对象的区别

  • 面向过程:主要关注的是实现的具体的过程,因果关系

    优点:对于业务逻辑比较简单的程序,可以达到快速开发,前期投入成本较低

    缺点:

    • 采用面向过程的方式开发很难解决非常复杂的业务逻辑,而且也很难关注到每个点的细节

    • 软件耦合度高,软件拓展力差

    • 组件代码复用性差

  • 面向对象:主要关注的是对象【独立体】的能完成那些功能。

    优点:耦合度低,扩展力强。组件复用性强

    缺点:前期投入成本高,需要进行独立抽象,大量的系统分析和设计。

面向对象的三个生命周期:

  • OOA - 面向对象分析
  • OOD - 面向对象设计
  • OOP - 面向对象编程

类与对象

要想使用OOP,一定要清楚对象的三个主要特性:

  1. 对象的行为(behavior): 可以对对象施加哪些操作,或可以对对象施加哪些方法?

  2. 对象的状态(state): 当施加那些方法时,对象如何响应?

  3. 对象标识(identity):如何辨别具有相同行为与状态的不同对象?

对象的状态改变不是自发的,必须通过调用方法来改变。如果是自发改变,只能说明封装遭到了破坏。作为一个类的实例,每个对象的标识永远是不同的,状态常常也存在着差异。

类的定义

类是对一个集合的抽象、是对事物分析结构后抽象出来的模板。现实世界中,事物A和事物B之间具有共同的特征,进行抽象总结出的一个模板,这个模板被称为类。

语法结构:

JAVA 复制代码
[修饰符列表] class 类名{
    //属性(对事物状态信息的抽象) 属性的类型为java所有的数据类型 
    [属性修饰符] 数据类型 变量名; //这里也可以直接赋值初始化
    //方法
    [属性修饰符] 返回值类型 方法名(参数列表){
        //方法体
    }
}

java中所有的类都是引用类型

识别类的简单规则是在分析问题的过程中寻找名词 ,而方法对应着动词。例如,在订单处理系统中,有这样一些名词:

  • 商品(Item)
  • 订单(Order)
  • 送货地址(Shipping address )
  • 付款(Payment)
  • 账户(Account)

这些名词很可能成为类Item、Order等。接下来,查看动词:商品被添加到订单中,订单被发送或取消,订单货款被支付。对于每一个动词如:"添加"、"发送"、"取消"以及"支付",都要标识出主要负责完成相应动作的对象。

例如,当一个新的商品添加到订单中时,那个订单对象就是被指定的对象,因为它知道如何存储商品以及如何对商品进行排序。也就是说,add应该是Order类的一个方法,而Item对象是一个参数。

对象

对象是实际存在的个体,是从类中创建出来的实例。

类与对象的关系

类-----实例化------>对象

对象又被称类的为实例(instance)

对象---抽象---->类

类描述的是对象的共同特征,这些特征包括:

类的状态--->对事物**状态信息(啥样子)的抽象--->类的属性
类的动作信息--->对事物
能力(能干啥)**的抽象 --->一个类的方法

不同对象对应的状态的数值和动作的结果是可能不一样的

对象的创建和使用

静态变量:可以通过类直接访问, 用static 修饰,存储在方法区内存当中。

实例变量(成员变量、域):不能通过类直接访问的类的变量,不创建对象这个实例变量在JVM中堆内存中不会在内存中分配区域。如果没有手动赋值,系统赋默认值。

java 复制代码
数据类型          				默认值
--------------------------------------------
byte, short, int, long         0
double, float                  0.0
boolean                        false
char                           \u0000
引用数据类型                     null(空值)
对象的创建

一个类可以实例化N个对象,实例化是通过new关键字去创建的,语法为:

java 复制代码
引用数据类型 变量名 = new 方法名(实参数列表...); //这里的这个变量名 称之为对象的引用,所以叫做对象引用变量。 方法名成和类名一致。

方法区: 字节码文件(class文件)在类加载时候,将其放在内存的方法区当中(只存一份)

栈区:方法代码执行的时候,会给该方法分配内存空间,在栈空间压栈。主要用来存储方法的局部变量。

堆区:new对象在堆内存中存储。

new操作符的返回值也是一个引用。下列语句:

Date deadline = new Date();

有两个部分。表达式new Date()构造了一个Date类型的对象,并且它的值是对新创建对象的引用,这个引用存储在变量deadline中。局部变量不会自动地初始化为null,而必须通过调用new或将它们设置为null进行初始化。

代码内存分析1

TestObj1.java 里的代码

java 复制代码
public class TestObj1 {
	public static void main(String[] args){
		int i = 10; //i 局部变量
        //这里 s 是局部变量
		Student s = new Student();
        s.no = i;
        //再次创建变量
        Student stu = new Student();
		System.out.println(s.no);
		System.out.println(stu.no);
	}
}

Student.java 里的代码

java 复制代码
public class Student {
	int no;
	int age;
	boolean sex;
	String addr;
}

内存分析图:

总结:对象的实质是通过new运算符在堆内存中开辟的内存空间,这块内存空间的地址(逻辑地址)赋给一个变量,这个变量称之为引用变量。类的成员变量(实例变量)随着类被实例化后在堆内存中对应的实例对象中存储。每个new出来的对象对应内存中的一份内存。

java语言中没有指针,程序员不能直接操作堆内存。程序员只能通过"引用"去访问堆内存当中的实例的变量。访问变量格式:

JAVA 复制代码
去读数据:对象引用变量.对象变量名
修改数据:对象引用变量.对象变量名 = 值;
代码内存分析2

TestObj2.java 里的代码

java 复制代码
public class TestObj2 {
	public static void main(String[] args){
		//定义一个局部变量u,数据类型为User的引用。
		User u = new User();
		
		System.out.println(u.no); 
		System.out.println(u.name);
		System.out.println(u.addr);
		
		u.no = 110;
		// "jack"是一个String对象,u指向的对象(new出来的User的对象)的属性name中存储的是jack的内存地址,也就是说这个对象的name属性也是个引用
		u.name = "jack"; 
		u.addr = new Address();
		
		System.out.println(u.name + "居住在哪个城市" + u.addr.city);
		System.out.println(u.name + "居住在哪个街道" + u.addr.street);
		System.out.println(u.name + "邮编是" + u.addr.zipcode);
		
	    u.addr.city = "北京";
		u.addr.street = "朝阳街道区";
		u.addr.zipcode = "11111111";
		
		System.out.println(u.name + "居住在哪个城市" + u.addr.city);
		System.out.println(u.name + "居住在哪个街道" + u.addr.street);
		System.out.println(u.name + "邮编是" + u.addr.zipcode);
        
        Address a = u.addr;
		a.city = "西安";
		System.out.println(u.addr.city);
		System.out.println(a.city);

	}
}

class User {
	//类的属性的类型可以为java的所有数据类型
	int no;
	//String 是一个对象引用类型
	String name; //这里表示这个User类的属性name是一个String的引用。
	//addr 是一个Address对象的引用类型
	Address addr; //这里表示这个User类的属性addr是一个Address的引用类型
}

class Address {
	String city;
	String street;
	String zipcode;
}

内存分析图:

JVM内存总结:

jvm主要包括三块内存空间,分别为:堆空间,栈空间,方法区。堆和方法区空间都各存在一份,而栈空间是一个线程一个栈空间。当方法调用的时候,该方法所需要的空间在栈空间内存中分配,称为压栈。方法执行结束之后,该方法所属的内存空间释放,称之为弹栈。在栈空间主要存储方法的局部变量。当程序开始运行时,方法的代码片段以及整个类代码片段都会存储到方法区当中,在类加载的时候这些代码片段会载入。在程序执行的过程中,使用new运算符创建的对象会存储在堆内存中,主要存储的是对象的实例变量。类的静态变量存储在方法区当中。当堆内存中java对象没有更多的引用指向它或者此对象无法访问时,会被垃圾回收器回收。

空指针异常

空引用访问"实例"相关的数据时候会发生空指针异常

java 复制代码
public class TestObj3 {
	public static void main(String[] args){
		User u = new User();
		System.out.println(u.no); //0
		
		u = null;
		//会抛出异常: java.lang.NullPointerException
		//空引用访问"实例"先关的数据时候会发生空指针异常
		System.out.println(u.no); //0
	}
}
总结:

对象:使用new运算符在堆内存中开辟的内存空间。

引用:是一个变量,保存了指向堆内存中的对象的内存地址。

创建一个对象没有用变量接受,称之为匿名对象。如:new Test().add()。匿名对象只能使用一次,使用完后立即被销毁。

构造方法

构造方法/构造器/Constructor

构造方法语法结构:
java 复制代码
[修饰符列表] 构造方法名(形式参数列表) {
	//方法体
}
使用细节:
  1. 构造方法没有返回值。

  2. 构造方法名称和类名必须保持一致。

  3. 当我们在类中没有写构造器使,这种情况下,编译器在编译的时候会默认加上一个无参且方法体为空的构造方法。但是,如果类的构造函数被重写了(类中有有参构造方法), 这时编译器就不会再给它默认加上一个无参且方法体为空的构造函数.可以理解为无参的构造方法被覆盖了。

    如果类中提供了至少一个构造器,但是没有提供无参数的构造器,则在构造对象时如果没有提供参数就会被视为不合法。new无参构造器创建对象会报错。建议开发中手动为当前类创建无参构造方法,因为无参构造方法太常用了。

  4. 构造方法不能被static、final、synchronized、abstract和native修饰

  5. 构造方法支持重载机制。

  6. 使用this(形参)可以调用其他构造方法,必须放在构造方法第一行。

构造方法的作用:

初始化实例变量的内存空间,给实例变量赋值。

java 复制代码
public class Account {
    //账号
    private String account;
    //余额
    private double balance;

    public Account() {
        //无参构造方法初始化实质
        //account = null;
        //balance = 0.0;
    }

    public Account(String account) {
        this.account = account;
    }

    public Account(String account, double balance){
        this.account = account;
        this.balance = balance;
    }
}

在执行构造器之前,先执行赋值操作。当一个类的所有构造器都希望把相同的值赋予某个特定的实例域时,这种方式特别有用。初始化实例域时,初始值不一定是常量值(也有可能是一个代码块,方法)。

this关键字

this可以翻译为"当前对象",其实代表当前正在执行这个行为/动作的对象。

this的内存分析:

this是一个引用,是一个变量,this变量中保存了内存地址指向了自身对象,this 存储在JVM堆内存对象内部。每一个对象的内存中都有this且不相同。

为什么要使用this变量

在代码运行时候,当某个对象执行某个实例方法(执行动作)过程中,想访问当前对象一些属性或别的实例方法。我们一般采用的方式引用.xxx。但是这个引用在不同对象访问这个实例方法必须代表对应的对象(比如多态),new 出来的对象无法满足。 所以this关键字诞生了。所以,this代表当前正在执行这个行为/动作的对象。

在每一个方法中,使用this关键字接受隐式参数。

this 是可以省略的, 什么时候不能省略?

用来区分局部变量和实例变量的时候,"this."不能省略。

this可以用在哪里

可以使用在实例方法当中,代表当前对象【语法格式:this.】

可以使用在构造方法当中,通过当前的构造方法调用其他构造方法【语法格式:this(实参)】,this()只能出现在构造函数第一行。

java 复制代码
public class ThisTest2 {
    public static void main(String[] args) {
        ThisTest2.doSome();
        ThisTest2 t2 = new ThisTest2();
        t2.doSome();
        t2 = null;
        //带有static的方法,其实既可以采用类名方式访问,也可采用引用方式访问。但是即使采用引用方式的访问,实际上执行的时候
        //和引用指向的对象无关。
        t2.doSome(); //这里不会出现空指针异常。
    }

    public static void doSome(){
        System.out.println("do some");
    }
}
相关推荐
weixin_531094692 小时前
heap_4内存管理
java·开发语言·算法
疯狂的维修2 小时前
关于用C#写电机起保停控制
开发语言·c#
程序员南飞2 小时前
win系统安装minio
java·数据结构·算法
Escalating_xu2 小时前
【Python】使用库:标准库、第三方库、pip、项目实战与程序打包
java·python·pip
Wang's Blog3 小时前
Java框架 SpringCloud 快速入门: 过滤器链执行顺序
java·spring cloud
geovindu3 小时前
rust: Abstract Factory pattern
开发语言·后端·设计模式·rust·抽象工厂模式
砚底藏山河3 小时前
量化实战:回测数据底座快照与版本管理进阶
java·大数据·python·金融·maven
“AI国潮设计-小江”3 小时前
《Python+SDXL实战:用ControlNet批量生成“英歌舞麻将糕”IP,附自动化脚本与商用思路》
开发语言·人工智能·python·prompt·aigc
Cc.Y3 小时前
Java零基础入门:封装与继承 —— 从“裸奔“到“穿衣服“,从“重复造轮子“到“站在巨人肩膀上“
java·开发语言