第一章 类与对象
面向对象简介
面向过程指的是面对于一个问题的解决方案,更多情况下不会做出重用的设计。
面向对象主要设计形式为模块化设计,可以进行重用配置,更多情p况下考虑的是标准,然后根据标准进行拼装。
面向对象有三个主要特性:
封装性:内部的操作对外部不可见,当内部的操作都不可直接使用的时候才是安全的;
继承性:在已有结构的基础上继续进行功能的扩充;
多态性:在继承性的基础上扩充而来的概念,指的是类型的转换处理。
面向对象程序开发的步骤:
OOA:面向对象分析;OOD:面向对象设计;OOP:面向对象编程。
类与对象简介
类是对某一类事物共性的抽象概念。
对象描述的是一个具体的产物。
类是一个模板,而对象是类的实例,所以先有类才有对象。(类比为船的图纸(类)与船(对象),根据图纸可以造很多船)
类的组成:
成员属性(Field):简化称为”属性“,其对象的特征(人的姓名、人的年龄...),属性是指描述对象的一组数据,表现为对象的一些变量。
操作方法(Method):表示对象的行为或所作的工作。
类与对象的定义及使用
Java中类是一个独立的结构体,所以需要用class来定义
public class Student {
// 类的属性:成员变量
String name;
int age;
String like;
// 成员方法(实例方法)
public void show(){
System.out.println("我叫"+name+",今年"+age+",我的爱好"+like);
}
// main方法
public static void main(String[] args) {
Student stu = new Student(); //创建对象,通过类来创建对象 new 类名()
stu.name = "张三"; // 调用成员变量赋值:对象名.属性名
stu.age = 20;
stu.like = "干饭";
stu.show(); // 调用成员方法
}
}
语法:
产生对象:
一步完成: 声明并实例化对象:类名称 对象名称 = new 类名称();
分步完成: 声明对象:类名称 对象名称 = null;
实例化对象:对象名称 = new 类名称();
调用: 类中的属性:对象名称.成员属性
类中的方法:对象名称.方法名称()
如果程序没有进行对象属性内容的设置,则数据内容为其对应数据类型的默认值。
对象内存分析
Java中类属于引用数据类型,困难之处在于要进行内存的管理。所以对内存管理进行简单分析。
最常用的内存空间:
堆内存:保存对象的具体信息,在程序中堆内存空间的开辟是通过new完成的;
栈内存:保存单块堆内存的地址。即:通过通过地址找到堆内存,而后找到对象内容。但是为了分析简化起见,可以简单的理解为:对象名称保存在了栈内存之中。
![](https://img-blog.csdnimg.cn/20210710084718464.png)
直接声明并实例化对象的内存分析:
class Person { // 定义一个类
String name; // 人的姓名
int age; // 人的年龄
public void tell() {
System.out.println("姓名:" + name + ",年龄:" + age);
}
}
public class JavaDemo {
public static void main(String[] args) {
Person per = new Person(); // 声明并实例化对象
per.name = "张三";
per.age = 18;
per.tell(); // 进行方法的调用
}
}
![](https://img-blog.csdnimg.cn/202107100856016.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NDIyNzczMw==,size_16,color_FFFFFF,t_70)
![](https://img-blog.csdnimg.cn/20210710085654213.png)
分步骤声明再实例化对象的内存分析
public class JavaDemo {
public static void main(String[] args) {
Person per = null; // 声明对象
per = new Person(); // 实例化对象
per.name = "张三";
per.age = 18;
per.tell(); // 进行方法的调用
}
}
![](https://img-blog.csdnimg.cn/20210710090040618.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NDIyNzczMw==,size_16,color_FFFFFF,t_70)
所有调用类的属性和方法在实例化对象完成后才能执行,调用只声明但没有实例化的对象
public class JavaDemo {
public static void main(String[] args) {
Person per = null; // 声明对象
per.name = "张三";
per.age = 18;
per.tell(); // 进行方法的调用
}
}
上述代码会产生“java.lang.NullPointerException”的空指向异常,因为此时并没有在堆内存中开辟空间,这种情况只发生于引用数据类型。
对象引用分析
由于类属于引用数据类型,因此就牵扯到内存的引用传递。
引用传递:指同一块堆内存被不同的栈内存所指向。
在主方法中使用引用传递:
public class JavaDemo {
public static void main(String[] args) {
Person per1 = new Person(); // 声明并实例化对象
per1.name = "张三";
per1.age = 18;
Person per2 = per1; // 引用传递
per2.age = 80;
per1.tell(); // 进行方法的调用
}
}
![](https://img-blog.csdnimg.cn/20210710090956516.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NDIyNzczMw==,size_16,color_FFFFFF,t_70)
利用方法实现引用传递:
与之前的差别最大的地方在于,此时的程序是将Person类的实例化对象(内存地址、属性的参数)传递到了change()方法中,由于传递的是一个Person对象类型,那么change()接收的也是Person类型。
public class JavaDemo {
public static void main(String[] args) {
Person per = new Person(); // 声明并实例化对象
per.name = "张三";
per.age = 18;
change(per); // 等价于:Person temp = per;
per.tell(); // 进行方法的调用
}
public static void change(Person temp) {
temp.age = 80;
}
}
![](https://img-blog.csdnimg.cn/20210710092715236.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NDIyNzczMw==,size_16,color_FFFFFF,t_70)
引用传递可以发生在方法上,要观察方法里面传的的参数类型,同时也要观察方法的执行过程。
引用与垃圾产生分析
引用传递的本质:一场堆内存的调戏游戏。但引用传递,引用不当会产生垃圾。对垃圾产生原因进行简单分析。
public class JavaDemo {
public static void main(String[] args) {
Person per1 = new Person(); // 声明并实例化对象
Person per2 = new Person();
per1.name = "张三";
per1.age = 18;
per2.name = "李四";
per2.age = 19;
per2 = per1; // 引用传递
per2.age = 80;
per1.tell(); // 进行方法的调用
}
}
![](https://img-blog.csdnimg.cn/20210710093616813.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NDIyNzczMw==,size_16,color_FFFFFF,t_70)
垃圾空间:没有任何栈内存所指向的堆内存空间,所有的垃圾都将被GC(Garbage Collector:垃圾收集器)不定期进行回收,并且释放无用空间,但如果垃圾过多,一定将影响到GC的处理性能,从而降低整体的程序性能。
一个栈内存只能够保存一个堆内存的地址数据,如果发生更改,则之前的地址数据将从此栈内存中彻底消失。
第二章 深入分析类与对象
成员属性封装
类的组成就是属性与方法,一般而言方法都是对外提供服务的,所以是不会进行封装处理的;而属性需要较高的安全性,所以就需要封装性对属性进行保护。
对属性进行封装:使用private关键字。
class Person { // 定义一个类
private String name; // 人的姓名
private int age; // 人的年龄
public void tell() {
System.out.println("姓名:" + name + ",年龄:" + age);
}
}
属性一旦封装后,外部将不可以直接进行访问。
【setter、getter】设置或取得属性使用setXxx()、getXxx()方法。
设置属性方法:public void setName(String name) {};
获取属性方法:public String getName() {};
class Person { // 定义一个类
private String name; // 人的姓名
private int age; // 人的年龄
public void tell() {
System.out.println("姓名:" + name + ",年龄:" + age);
}
public String getName() {
return name;
}
public void setName(String newName) {
name = newName;
}
public int getAge() {
return age;
}
public void setAge(int newAge) {
age = newAge;
}
}
public class JavaDemo {
public static void main(String[] args) {
Person per = new Person(); // 声明并实例化对象
per.setName("张三"); // 在类外部通过方法修改私有属性
per.setAge(18);
per.tell(); // 进行方法的调用
}
}
在定义类时,属性都应该使用private封装(98%),并提供setter、getter方法。
构造方法与匿名对象
现在的程序在使用类的时候一般都按照了如下的步骤进行:
声明并实例化对象,这个时候实例化对象中的属性并没有任何的的数据存在,都是其对应数据类型的默认值。
需要通过一系列的setter()方法设置属性值
public class JavaDemo{ // 主类
public static void main(String args[]){
// 1、对象初始化准备
Person per = new Person(); // 声明并实例化对象
per.setName("张三");
pei.setAge(-18); // 在类外部修改属性
// 2、对象的使用
per.tell(); // 进行方法的调用
}
}
假设现在类中的属性有很多个,按上述方法,需要调用多次setter方法进行设置,过于麻烦,所有提供构造方法,实现实例化对象中的属性初始化处理。
构造方法作用:实现实例化对象中的属性初始化处理。 使用:通过new关键字进行实现(因为实例化对象就是通过new关键字实现的)。
定义要求:
构造方法名称必须与类名称一致;
构造方法不允许设置返回值类型,即:没有返回值定义;
构造方法时在使用new关键字实例化对象时自动调用的。
class Person { // 定义一个类
private String name; // 人的姓名
private int age; // 人的年龄
public Person(String n, int a) { // 定义有参构造
name = n; // 为类中的属性赋值(初始化)
age = a; // 为类中的属性赋值(初始化)
}
public void tell() {
System.out.println("姓名:" + name + ",年龄:" + age);
}
}
public class JavaDemo { // 主类
public static void main(String[] args) {
// 1、对象初始化准备
Person per = new Person("张三", 18); // 声明并实例化对象
// 2、对象的使用
per.tell(); // 进行方法的调用
}
}
实例化对象例子对比:
之前:①Person ②per= ③new ④Person()
当前:①Person ②per= ③new ④Person(“张三”, 18)
解析:
①Person:定义对象的所属类型,类型决定了可以调用的方法;
②per=:实例化对象的名称,所有的操作通过对象来进行访问;
③new:开辟一块新的堆内存空间;
④Person():调用无参构造; ④Person(“张三”, 18):调用有参构造。
在Java程序里面考虑到程序结构的完整性,所以所有的类都会提供有构造方法,如果你的类中没有定义的构造方法,那么程序会默认提供无参的构造方法,这个是在程序编译时自动创建的;
如果你的类中有定义构造方法,那么程序则不会自动提供无参的构造方法。所以一个类至少存在有一个构造方法,且永恒存在。
疑问:为什么构造方法上不允许设置返回值类型?
程序编译器是根据代码结构来进行编译处理的,执行时也是根据代码结构来处理的。
因此如果设置了返回值类型,那么构造方法的结构与普通方法的结构完全相同,这样编译器会认为此方法时一个普通方法。
普通方法与构造方法最大的区别:构造方法是在类对象实例化时调用的,而普通方法是在类对象实例化产生之后才可以调用的。
构造方法本身就是一个方法,其具有重载的特点,而构造方法重载的时候只需要考虑参数的类型及个数即可。
进行多个构造方法定义的时候可以有定义的顺序,如按照参数的个数降序或升序进行。
构造方法可以进行数据的设置,而setter也可以进行数据的设置。构造方法是在对象实例化的时候为属性设置初始化内容,而setter还拥有修改数据的内容。
匿名对象:直接实例化对象来进行类的操作(没有声明对象,所以对象是没有名字的)。
public class JavaDemo { // 主类
public static void main(String[] args) {
new Person("张三", 10).tell(); // 通过匿名对象直接进行方法的调用
}
}
通过匿名对象调用方法,由于此对象没有任何的引用名称,因此该对象在使用一次之后就成为了垃圾,而所有的垃圾都将被GC进行回收与释放。
通过以下程序,进行简短的内存分析。
class Message{
private String title;
public Message(String t) {
title = t;
}
public String getTitle() {
return title;
}
public void setTitle(String t) {
title = t;
}
}
class Person{
private String name;
private int age;
public Person(Message msg,int a ) {
name = msg.getTitle();
age = a;
}
public Message getInfo(){
return new Message(name + ":" + age)
}
public void tell() {
System.out.println("姓名" + name + "年龄" + age);
}
}
public class No1PersonMessage {
public static void main(String args[]) {
Message msg = new Message("mldn");
Person per = new Person(msg,20);
msg = per.getInfo();
System.out.println(msg.getTitle());
}
}
![](https://img-blog.csdnimg.cn/20210710214551671.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NDIyNzczMw==,size_16,color_FFFFFF,t_70)
只要是方法都可以传递任意的数据类型(基本数据类型、引用数据类型)。
this关键字
this可以实现以下三类 结构的描述:
当前类中的属性:this.属性名;
当前类中的方法:构造方法:this();普通方法:this.方法名();
描述当前对象;
在Java程序中,“{}”是作为一个结构体的边界符,所以在程序里面当进行变量(参数 / 属性)使用时,都会以“{}”作为查找边界。按照就近取用的原则,此时的构造方法并没有能够访问类中的属性,所以为了明确标记类中的属性和参数的区别,往往会在属性前追加一个this
,表示本类的属性。
只要是访问本类中的属性的时候,都要加this。
class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age
}
public void tell() {
System.out.println("姓名:" + this.name + ",年龄:" + this.age);
}
}
待续。。。