6.面向对象(基础)
6.1 类与对象
类是一种数据类型,是由程序员自己定义的。包含有属性、方法。
对象是类的实例化,如创建一个cat类(猫),小明的猫是其中一个对象,小王的猫是一个对象…。
- 类是抽象的,概念的,代表一类事物。如:猫类、人类、狗类…即它是数据类型。
- 对象是具体的,实际的,代表一个具体事物,是实例。
- 类是对象的模板,对象是类的一个个体,对应一个实例。
对象在内存中存在形式
创建cat对象且共有三个属性,名字=小白,年龄=12,花色=白色;
创建一个对象内存发生了什么?
- 先加载类信息(属性和方法信息,只会加载一次)
- 在堆中分配空间,进行默认初始化(看规则)
- 把地址赋给对象名(cat),对象名指向对象
- 进行初始化,比如cat.name = “小白”;
注意:
JDK1.7 及之后版本的 JVM 已经将运行时常量池从方法区中移了出来,在 Java 堆(Heap)中开辟了一块区域存放运行时常量池。
JDK1.8开始,取消了Java方法区,取而代之的是位于直接内存的元空间(metaSpace)。
6.2 属性/成员变量
属性是类的一个组成部分,一般是基本数据类型,也可是引用类型(对象,数组)。
属性定义:
// 格式:访问修饰符 属性类型 属性名
// 访问修饰符不写默认为public
class Person {
public String name;
public int age;
}
属性如果不赋值,有默认值,规则和数组一致。
6.3 栈、堆、方法区
-
栈:一般存放基本数据类型(局部变量)
-
堆:存放对象、数组等
-
方法区:常量池(常量,比如字符串),类加载信息
JDK1.8开始,取消了Java方法区,取而代之的是位于直接内存的元空间(metaSpace)。
6.4 成员方法
创建方法:
//访问修饰符 返回数据类型 方法名(参数类型 参数名,参数类型 参数名,,,,) {
// 方法体
// }
public void speak(){
System.out.println("我是一个好人。");
}
调用方法:
//首先创建对象
Preson p1 = new Person();
//调用方法
p1.speak();
方法调用机制
- 当程序执行到方法时,就会开辟一个独立的空间(栈空间)
- 当方法执行完毕,或者执行到return语句时,就会返回
- 返回到调用方法的地方
- 返回后,继续执行后面的代码
- 当main方法(栈)执行完毕,整个程序退出
-
同类之间方法调用,直接调用即可,如:
public class MethodDetail01{ public static void main(String[] args){ Tool to = new Tool(); to.sayOk(); } } class Tool{ //同一个类中方法互相调用直接调用 public void print(int num){ System.out.println("print方法输出:" + num); } public void sayOk(){ print(666);//调用print方法 System.out.println("继续执行sayOk"); } }
-
不同类之间方法调用需要先创建类的对象,再调用方法,如:
public static void main(String[] args){ Naa nn = new Naa(); nn.sayBye(); } } class Tool{ public void print(int num){ System.out.println("print方法输出:" + num); } } class Naa{ public void sayBye(){ Tool tt = new Tool();//创建对象 tt.print(999);//调用方法 System.out.println("执行sayBye"); }
6.5 成员方法传参
一、数据类型转换
- 传参时,如果可以自动数据类型转换,实参和形参数据类型可以不同
- 返回数据类型和返回值同上
二、引用类型传参——注意思考内存图
引用类型传递的是地址(传递的也是值,但是值是地址),可以通过形参影响实参
代码示例
public class MethodParameter { public static void main(String[] args) { Person a = new Person(); a.age = 10; a.name = "faker"; A obj = new A(); obj.test(a); System.out.println(a.age); // 155 引用类型传参 } } class Person { int age; String name; } class A { public void test(Person p) { p.age = 155; } }
6.6 递归
含义:方法自己调用自己,每次调用时传入不同的参数。
案例:
-
阶乘:
class getNum{ public int printNum(int num){ if(num == 1){ return 1; }else{ return printNum(num - 1) * num; } } }
6.7 方法重载
java中允许同一个类中,多个同名方法存在,但要求 形参列表 不一致。
- 方法名:必须相同!
- 参数:必须不同!(可以是个数、顺序不同,至少有一样不同)
6.8 可变参数
java允许将同一个类中多个同名同功能但参数个数不同的方法,封装成一个方法。
基本语法:
// 访问修饰符 返回类型 方法名(数据类型... 形参名) {}
public int varSum(int... nums) {
int sum = 0;
for(int i = 0; i < nums.length; i++) {
sum += nums[i];
}
return sum;
}
细节:
- 可变参数的实参可以是0个或任意多个。
- 可变参数的实参可以是数组。
- 可变实参的本质就是数组。
- 可变参数可以和普通类型的参数一起放在形参列表,但必须保证可变参数放在最后!
- 一个形参列表中只能出现一个可变参数。
6.9 构造器/构造方法
格式:
// [修饰符] 方法名(形参列表) {};
class Person{
String name;
public Person(){ //默认无参构造器
name = "张三";
}
public Person(String pName){ //有参构造器
name = pName;
}
}
说明:
- 构造器的修饰符可以是默认,也可以是public、protected、private
- 构造器没有返回值(也不写返回值类型)
- 方法名必须和类名一样
- 构造器可以重载
- 不写构造器有一个默认的无参构造器,一旦定义了自己的构造器,默认的构造器就被覆盖了,不能再使用默认的无参构造器,除非显式的的定义一下。
- 构造器是完成对象的初始化,并不是创建对象
- 创建对象时,系统自动调用该类的构造方法
6.10 对象创建流程
代码如下:
class Person {
int age = 90;
String name;
Person(String n, int a) {
name = n;
age = a;
}
}
Person p = new Person("小明", 20);
流程分析:
- 加载Person类信息(Person.class),只会加载一次
- 在堆中分配空间
- 完成对象初始化
- 默认初始化—age = 0, name = null
- 显示初始化—age = 90, name = null
- 构造器初始化—age = 20, name = 小明
- 对象在堆中的地址返回给p(p是对象名,或者说对象引用)
6.11 this
java虚拟机会给每个对象分配this,代表当前对象。可以把this理解为对象中一个指向自身地址的属性。
this使用细节:
- this关键字可以用来访问本类的属性、方法、构造器
- this用于区分当前类的属性和局部变量
- 访问成员方法的语法:this.方法名(形参列表)
- 访问构造器语句:this(形参列表);注意只能在构造器中使用,必须放置在第一条语句
- this不能在类的外部使用,只能在类定义的方法中使用