一、创建对象的内存理解
创建对象的步骤
(1)将要创建对象的所属类型的字节码文件加载到方法区中
(2)在栈内存中创建一个Person类型的引用,将来用于存储在堆内存中对象的地址
(3)在堆内存中开辟内存空间,给成员变量分配内存
(4)给对象中成员变量进行默认初始化赋值
(5)将堆内存中开辟好内存的地址,赋值给对象名
说明:
(1)第一个使用某个类型的时候,会将该类型的字节码文件加载进方法区,第二次或者以后再进行时候的时候,不需要重复加载
(2)在创建对象的时候,默认隐含一个字节码地址,将来可以通过该对象找到对应自己的字节码对象
(3)那个对象调用方法,在方法的栈帧中,就默认隐含了该对象的地址
(4)只要看到new,就是在堆内存中新开辟一段内存空间。
两个引用指向同一个对象
二、面向对象中的其他内容
成员变量和局部变量的比较
1、成员变量:定义在类中成员位置的变量,就是成员变量
2、局部变量:定义在类中方法中的局部位置的变量,就是局部变量
2、不同点:
代码层面:定义的位置不同
成员变量:类中方法外
局部变量:类中方法中
内存层面:空间不同
成员变量:属于对象,只有在创建完对象才能使用成员变量,和对象的内存位置相同,在堆内存中
局部变量:属于方法,在方法的栈帧中
内存层面:生命周期
成员变量:随着对象的创建而存在,随着对象的消失而消失。(栈内存中没有任何引用指向该对象的时候)
局部变量:随着方法的调用而存在,随着方法的调用结束而消失
初始化状态不同:
局部变量:没有默认的初始值,必须先进行初始化赋值,才能使用
成员变量:有默认的初始化值。
基本类型:
整数类型:0
小数类型:0.0
字符类型:‘\u0000’
布尔类型:false
引用类型:null
//成员变量和局部变量的比较
public class Demo_3 {
// 成员变量
int a;
double d;
char c;
boolean boo;
String str;
public static void main(String[] args) {
Demo_3 de = new Demo_3();
System.out.println(de.a);
System.out.println(de.d);
System.out.println(de.c);
System.out.println(de.boo);
System.out.println(de.str);
// 局部变量 ,没有进行初始化,就不能进行使用
int b = 10 ;
System.out.println(b);
}
}
匿名对象
1、匿名对象:
没有名字的对象
2、格式:
new 类名();
3、匿名对象使用的场景
(1)只想调用一次此对象中的方法,这个时候就可以使用匿名对象来进行调用,
new Car().playFriend();
(2)匿名对象可以作为某个方法的实际参数。这种调用格式,虽然在主方法中,可以看做是一个匿名对象,但是在被调用的方法中,这个对象是有引用的对象,不是匿名对象
useCar(new Car());//匿名对象
(3)可以作为某个方法的返回值数据。进行返回。这种调用格式而言,虽然在被调用者方法中,可以看做一个匿名对象,但是在调用者看来,这个对象可能不是匿名对象
4、注意:
匿名对象是否可以给成员变量赋值。可以给成员变量赋值,但是没有意义,因为栈内存,没有任何一个引用保存匿名对象的地址,赋完值该对象就成为了垃圾对象,无法访问到的
public class Demo_4 {
public static void main(String[] args) {
// 匿名对象
Car c = new Car();//正常情况,有名字的对象,对象的名字c
c.brand = "BMW";
c.price = 500000;
// c.run();
// 没有名字的对象:匿名对象
new Car();
// 使用场景
// (1)将来对于某个对象在创建之后,只想调用一次此对象中的方法,这个时候就可以使用匿名对象来进行调用,
new Car().playFriend();
// (2)匿名对象可以作为某个方法的实际参数。这种调用格式,虽然在主方法中,可以看做是一个匿名对象,但是在被调用的方法中,这个对象是有引用的对象,不是匿名对象
useCar(new Car());//匿名对象
// (3)可以作为某个方法的返回值数据。进行返回。这种调用格式而言,虽然在被调用者方法中,可以看做一个匿名对象,但是在调用者看来,这个对象可能不是匿名对象
Car car = makeCar();
car.brand = "保时捷";
car.price = 400000;
car.run();
// 注意事项
new Car().brand = "奔驰";
System.out.println(new Car().brand);
}
public static Car makeCar() {
// Car c = new Car();
// return c;
return new Car();
}
public static void useCar(Car car) {//Car car = new Car();
car.brand = "五菱神车";
car.price = 10000;
car.run();
}
}
class Car{
// 属性
String brand;
double price;
// 行为
public void run() {
System.out.println(brand + " " + price + "跑的真快");
}
public void playFriend() {
System.out.println("带上女朋友去兜风");
}
}
三、封装
1、封装:隐藏事物的属性和实现细节,对外提供公共的访问方式。
2、封装的好处:
隐藏了事物的实现细节
提高了安全性
3、封装的原则
隐藏事物的属性
对外提供公共的方法访问
private关键字
1、private,私有的
2、可以修饰的内容
修饰成员变量
修饰成员方法
修饰构造方法
修饰内部类
3、修饰之后的效果
被private修饰的成员,外界就不能直接进行访问了,只能在本类中进行访问
4、private这个关键字,就是封装的一种体现
protected ,也是一种封装的体现
public class Demo_5 {
// private关键字
public static void main(String[] args) {
Phone p = new Phone();
// p.brand = "Apple";
p.price = 12343;
p.show();
// p.call();
}
}
class Phone {
// private
private String brand;//只能在本类中进行访问
double price;
public void show() {
System.out.println(brand + " " + price);
}
private void call() {//被private修饰的方法,在其他类中也不能被调用
System.out.println("打电话");
}
}
Get和Set的方法
1、当属性被private修饰之后,外界就无法直接访问,所有就需要对外提供公共的访问方式,让外界可以间接的访问属性。对于当前类而言,就可以控制外界访问属性的方式。(让外界怎么访问,外界就只能怎么样去访问)
2、一般都是set方法,完成对成员变量的赋值,get方法完成对成员变量值得获取
public class Demo_6 {
public static void main(String[] args) {
Computer com = new Computer();
// System.out.println(bom.brand);
com.setPrice(19);
System.out.println(com.getPrice());
}
}
class Computer{
private String brand ;
private String color;
private double price;
// 对外提供公共的访问方式
public void setPrice(double d) {
// 控制外界访问price的权限
/*if(d < 0) {
System.out.println("你设置的值我这里不予许");
}else {
price = d;
}*/
price = d;
}
public double getPrice() {
// price += 1000;
return price;
}
public void setBrand(String b) {//间接完成对brand的赋值
brand = b;
}
public String getBrand() {//让外界间接的访问brand的值
return brand;
}
public void setColor(String c) {
color = c;
}
public String getColor() {
return color;
}
}
变量访问原则和this关键字
1、变量的访问原则:
变量的访问原则:(就近原则)
变量的定义(声明):带着数据类型的变量
变量的使用(访问):不带着数据类型变量
就近原则:在访问某个变量的时候,会先寻找最近的该变量的定义,如果找到了,就使用该变量,如果没有找到,才会向更远的位置去寻找该变量的定义
如果出现局部变量和成员变量同名的情况,一定是先使用局部位置定义的变量,如果没有,才会使用成员位置定义的变量
2、this关键字
表示当前类型当前对象的引用
那个对象调用this所在的方法,this代表的就是那个对象
如果出现了局部变量和成员变量同名的情况,还想区分出成员变量和局部变量。就可以使用this关键字,被加上this.的变量一定是成员变量
public class Demo_7 {
public static void main(String[] args) {
// 变量的访问原则:(就近原则)
// 就近原则:在访问某个变量的时候,会先寻找最近的该变量的定义,如果找到了,就使用该变量,如果没有找到,才会向更远的位置去寻找该变量的定义
// 如果出现局部变量和成员变量同名的情况,一定是先使用局部位置定义的变量,如果没有,才会使用成员位置定义的变量
// 变量的定义(声明):带着数据类型的变量
// 变量的使用(访问):不带着数据类型变量
Star s = new Star();
s.setName("王宝强");
s.setAge(200);
}
}
class Star {
private String name = "刘德华";
private int age = 100;
String n = "张益达";
// 提供get,set方法的,对外提供公共的访问方式,让外界间接去访问该类中的属性
public void setName(String name) {
this.name = name;
}
/*public void setName(String n) {
System.out.println("StarName " + name);
System.out.println("n " + n);
name = n;
System.out.println("StarName " + name);
}*/
public String getName() {
return name;
}
public void setAge(int age) {
System.out.println(age); // 200
// 如果出现了局部变量和成员变量同名的情况,还想区分出成员变量和局部变量。
// 就可以使用this关键字,被加上this.的变量一定是成员变量
this.age = age;
System.out.println(this.age);//100
}
public int getAge() {
return age;
}
}
构造方法概述
1、构造方法:构造函数,构造器
2、作用:就是给成员变量进行赋值,构造方法在创建对象的时候,会自动调用,等对象创建完之后,对象中的成员变量就已经有具体的值了。
3、构造方法的格式
修饰符 方法名称(参数列表){
方法体;
}
4、说明
(1)构造方法方法名称,必须和类名保持一致,大小写都一样
(2)构造方法没有返回值类型的,连void都没有
(3)构造方法没有return语句的。如果你要写一个return,就写一个return;
5、注意:
(1)构造方法,不需要手动的调用,会创建对象的时候自动的调用一次
(2)对象本身不能调用构造方法
(3)每创建一个对象,只能调用一次构造方法
(4)构造方法只能在创建对象的时候,对成员变量赋值一次,不能再次通过构造方法完成对成员变量值得修改。
public class Demo {
public static void main(String[] args) {
// 构造方法
Animal an = new Animal("老鼠");
System.out.println(an.getName());
an.setAge(12);
an.setAge(122);
an.setAge(142);
// an.Animal("mouse");对象本身不能调用构造方法
Animal an1 = new Animal("cat",23);
// 构造方法只能在创建对象的时候,对成员变量赋值一次,不能再次通过构造方法完成对成员变量值得修改。
// 如果想对该对象的成员变量值进行修改,那就是用set方法即可
System.out.println(an1.getName());
System.out.println(an1.getAge());
}
}
class Animal{
private String name;
private int age;
// 构造方法
public Animal() {
// 没有参数的构造
name = "狮子";
}
public Animal(String name) {//在创建对象的时候,传入具体的数据
this.name = name;
return;
}
public Animal(String name,int age) {
this.name = name;
this.age = age;
}
// get set方法
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setAge(int age) {
this.age = age;
}
public int getAge() {
return age;
}
}
构造方法的注意事项
1、构造方法是可以有参数的,也可以没有参数
没有参数的空参构造:外界无法传入具体的数据,只能成员变量赋固定值
有参数的构造方法:外界在去创建对象的时候,可以指定具体的成员变量的值,使用构造方法完成参数的传递,进行对成员变量的赋值的过程。
2、如果在类中没有定义任何的构造方法,那么系统会默认提供一个空参构造。
如果在类中定义了任何的一个构造方法,那么系统都不会再提供空参构造了。
如果在类中,即使不去使用空参构造,一旦定义有参构造的时候,建议也要写上空参构造。
3、构造方法的重载
在同一个类中,方法名相同,参数列表不同,与返回值类型无关。
构造方法的重载,方法名都是和类名一致,【参数列表的不同】,返回值类型没有
public class Demo_8 {
public static void main(String[] args) {
// 构造方法的注意事项
Animal an = new Animal("ffs");
System.out.println(an.getName());
}
}
class Animal{
private String name;
private int age;
// 构造方法
public Animal() {
name = "pig";
}
public Animal(String name) {//在创建对象的时候,传入具体的数据
this.name = name;
return;
}
public Animal(String name,int age) {
this.name = name;
this.age = age;
}
// get set方法
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setAge(int age) {
this.age = age;
}
public int getAge() {
return age;
}
}
创建对象三个初始化的过程
1、创建对象的过程中,有三个初始化的过程
默认的初始化
显示初始化
构造初始化
2、三者的顺序
通过直接对成员变量的赋值,发现打印的结果是显示初始化的结果,因此显示初始化在默认初始化之后
通过在构造方法中对成员变量赋值,发现答应的结果是构造初始中赋值后的结果,因此构造初始化在显示初始化之后
顺序:默认初始化,显示初始化,构造初始化
3、图示
(1)将对象的字节码文件加载到方法区
(2)在占内存中声明一个Studeng类型的引用stu。将来用来存储在堆内存中开辟的内存空间的地址
(3)在堆内存中开辟内存空间,成成员变量分配内存
(4)给成员变量进行默认初始化赋值
(5)给成员变量进行显示初始化赋值
(6)给成员变量进行构造初始化赋值
(7)将对象的内存地址,赋值给栈内存中声明的引用。