第八章 认识面向对象
了解面向对象,知道类和对象的区别,会进行类的定义
8.1.1 知识框架
8.2 面向过程和面向对象的区别
“面向过程”是一种以过程为中心的编程思想,简称OP,就是分析出解决问题所需要的步骤,然后用函数一一把这些步骤实现,使用的时候一一调用就可以了,所以面向过程关注点是在步骤上,并没有关注对象这个事物
“面向对象”是一种以对象为中心的编程思想,简称OO,随着计算机技术的不断提高,计算机被用于解决越来越复杂的问题。一切事物皆对象,通过面向对象的方式,将现实中的事物抽象成对象,同时面向对象能有效提高编程的效率,通过封装技术,可以像搭积木的一样快速的全新的开发一个新系统。面向对象将对象作为程序的基本单元,将程序和数据封装其中,以提高软件的重用性、灵活性和扩展性
8.3面向对象的三大特征:封装、继承、多态
8.4类和对象的概念
类是一种抽象的概念,是一类事物的统称,从类到对象称为类的实例化,比如说“狗”算是一类,那么我们可以实例化一条狗,并且根据“狗”类中的方法,来对狗这个对象进行一系列的动作,比如说以下代码:
首先,先构造狗类,在定义狗类中的方法,并在MyDog中创建一条狗的实例对象,给其名称大小赋值,并输出:
public class Dog {
private Integer size;
private String dogName;
public Integer getSize() {
return size;
}
public void setSize(Integer size) {
this.size = size;
}
public String getDogName() {
return dogName;
}
public void setDogName(String dogName) {
this.dogName = dogName;
}
@Override
public String toString() {
return "Dog{" +
"size=" + size +
", dogName='" + dogName + '\'' +
'}';
}
}
public class MyDog {
public static void main(String[] args) {
Dog dog = new Dog();
dog.setDogName("旺财");
dog.setSize(22);
System.out.println(dog);
}
}
这样我们就可以得到结果:
Dog{size=22, dogName='旺财'}
通过以上描述,我们可以知道:类=属性+方法,而属性描述的是状态,方法描述的是行为动作,属性就如上面狗类中的size,DogName。方法就是set,get,equals等方法。
8.5 类的定义
根据8.4,我们得知类=属性+方法,我们接下来定义一个Student类,学生类中包含学生姓名、学生学号、学生年龄、学生身高等等属性:
public class Student {
private Integer id;
private String name;
private Integer age;
private Float height;
}
第九章 对象的创建和使用
理解构造方法以及重载机制,通过构造方法可以完成对对象的创建,并且能够通过引用访问对象的内存,了解java虚拟机内存管理,能够画出程序执行过程的内存图,并了解空指针异常是如何发生的,以及方法调用时参数是如何传递的。
9.1.1知识框架
9.1.2对象的创建
上面我们创建了Student的类,下面我们创建一个Student的实例对象student:
public class StudentTest01{
public static void main(String[] args){
Student student = new Student();
}
}
上面的变量生成代码和int i 其实是一样的不过int是一种基本数据类型,所以在这里new给省略了。这样我们就创建了一个student对象,接下来就是对象的使用和访问:
public class StudentTest01{
public static void main(String[] args){
Student student = new Student();
Integer id = student.id;
String name = student.name;
Integer age = student.age;
Float height = student.height;
System.out.println(id,name,age,height);
}
}
由于对象中的属性并没有赋值,所以得到的结果是:
null
null
null
null
下面说一下,当对象的属性没有赋值时,系统会对实例变量的属性默认赋值,下面是默认值参考表:
数据类型 | 默认值 |
---|---|
byte | 0 |
short | 0 |
int | 0 |
long | 0L |
float | 0.0f |
double | 0.0 |
boolean | false |
char | \u0000 |
引用数据类型 | null |
9.2 对象创建和使用的深层次解密
9.3.1 java虚拟机内存管理(理解)
首先看一下java虚拟机是如何管理他的内存的,请看下图:
1、程序计数器:
1)可以看做当前线程所执行的字节码文件的行号指示器
2)线程私有的内存
2、Java虚拟机栈:
1)概念:描述的是java方法执行的内存模型,(每一个方法执行都会在栈内存中生成一个方法栈桢),用于存储局部变量表,操作数栈,动态链接等信息,每个方法从调用到完成的过程,就是一个栈桢从压栈到出栈的过程。
2)特点:线程私有,生命周期和线程相同,这个区域会出现两种异常:StackOverflowError异常:若线程请求的深度大于虚拟机所允许的深度。
OutOfMemoryError异常:若虚拟机可以动态扩展,如果扩展无法申请到足够的内存。
3、本地方法栈
1)概念:他与虚拟机栈发挥的作用是相似的,区别是java虚拟机栈为执行java方法服务,而本地方法栈是为本地方法服务的
2)特点:线程私有,也会抛出两类异常,StackOverflowError和OutOfMemoryError
4、java堆
1)概念:是被所有线程共享的一块区域,在虚拟机启动时创建。
2)特点:线程共享,存放的是对象实例(所有的实例对象和数组),GC(垃圾回收器)管理的主要区域
5、方法区
1)概念:存储已被虚拟机加载的类信息、常量、静态变量等数据
2)特点:线程共享的区域,抛出OutOfMemory异常:当方法区无法满足内存分配需求的时候。
总结:在java虚拟机中,主要存在三个主要的区域:栈区、方法区、堆区。其中栈区中存放的是方法的栈桢,方法区中主要存放的是类的字节码文件,堆区中主要存放的是实例对象的引用。并且,一个线程有一个栈区,两个线程有两个栈区,每一个栈区存放的是不同线程上调用的方法的栈桢。但是所有的线程共用一个堆区和一个方法区。
结合上面的文字信息,我们画一张JVM虚拟机的内存管理简图:
通过一段代码,来看一看以下代码在执行过程中,内存是如何变化的
9.3.2 构造方法Constructor
构造方法是类中比较特殊的一类方法,我们只有通过调用构造方法才能创建类的实例对象,此外我们还可以通过构造方法直接给对象的属性赋值。
下面首先看一下构造方法的定义,请看以下的语法格式:
【修饰符列表】 构造方法名(形式参数列表){
构造方法体;
}
下面列几个构造方法的准则
1、构造方法名必须与类名一致
2、构造方法用来创建对象,以及完成属性初始化操作
3、构造方法的返回值类型不需要写,包括void关键字也不能写
4、构造方法的返回值类型实际上就是当前的类的类型
5、一个类中可以有多个构造方法,实际上这些构造方法构成了方法重载
看下面一段代码:
public class Student{
int age;
String name;
float height;
int id;
public static Student(){};
public static Student(int age;String name;int id; float height){
this.age = age;
this.name = name;
this.id = id;
this.height = height;
}
}
public class StudentTest01{
public static void main(String[] args){
Student student1 = new Student();
Student student2 = new Student(10,"张飞",1001,178);
}
}
以上这段代码的例子中存在两个构造方法,一个是无参数构造,一个是有参数构造,这两个方法时符合方法的重载机制的。所以一个类中的所有构造方法是构成重载的。无参数构造一般来说是需要用类中的set和get方法来给属性赋值,而有参数构造相当于给属性赋了一个初始值。
在后面的应用中,一般来说有参数的构造方法用的比较多。
此外,当一个类中没有构造方法时,系统默认类中存在无参数的构造方法,但是当写了有参数的构造方法之后,默认的无参数构造方法就不存在了,这个时候只要写了有参数的构造方法,就一定要把无参数构造方法同样写出来。
9.3.3 空指针异常
请看以下代码:
public class People{
int id;
String name;
int age;
char sex;
public People(){
}
public People(int id,String name,int age,char sex){
this.id = id;
this.name = name;
this.age = age;
this.sex = sex;
}
}
public class PeopleTest(){
public static void main(String[] args){
People people = new People(1001,"张飞",17,'男');
System.out.println("id = " + people.id);
System.out.println("姓名 = " + people.name);
System.out.println("年龄 = " + people.age);
System.out.println("性别 = " + people.sex);
people = null;
System.out.println("" + people.name);
}
}
上面这段程序,运行到最后一定会发生空指针异常NullPointerException,原因是引用people中存放的是对象在堆内存中的内存地址,但是在程序中people被赋值null,意味着对象的内存地址没有了JVM虚拟机不知道了地址,就找不到对象,所以报错空指针异常。但是虽然没有地址指向创建的people对象,但是在堆区中,实例化的对象people是存在的,这时候,对象只能静待垃圾回收器将其回收。
9.3.4 String对象
String对象不属于八大基本数据类型,而是属于引用数据类型,因此,String name = “张飞”,其中name存放的实际上并不是"张飞"这个字符串,而是这个字符串对象在堆区中的地址,所以一般做字符串比较的时候并不用“==”比较运算符,而是使用String类中的equals()方法,这样比较的就是字符串内容,而不是比较内存地址了。
那为什么有时候会发现下面的运算结果为true呢,不是说“恒等”比较的是内存地址吗。这是因为"张飞"这个字符串变量并不是只存储在堆区当中,此外还存储在方法区中的字符串常量池当中的,这样当我们用到“张飞”这个字符串的时候,JVM虚拟机就会从字符串常量池中获取这个对象,并将这个对象在堆中的地址赋值给引用。这样两个引用对应的内存地址就会是一样的,所以结果true,但并不是所有的字符串对象都会在字符串常量池中存在。他的内存也是有限的,所以我们一般比较字符串或者其他的引用数据类型的时候还是需要用equals()方法。
String name1 = "张飞";
String name2 = "张飞";
System.out.println(name1==name2);
9.3.5 方法返回值详解
方法返回值可以是任意一种数据类型,包括基本数据类型和引用数据类型,如果方法执行结束不返回任何数据,那么返回值类型必须要写void。并且只要写了返回值类型,那么就一定要有return语句返回数据,并且代码是按照自上而下的顺序执行的,一旦执行到了return语句,那么方法就会结束,所以return后面不应该存在代码。
其次当使用if…else语句时,你要看是否代码又返回数据,有时候需要写两个或者更多的return确保有返回值返回。
public static Integer sum(Integer a,Integer b){
if(a + b == 10){
return a + b;
}
return a - b;
}