友友们,互三啦!!互三必回!!背景踢踢哦~
更多精彩:个人主页
更多java笔记系列:java学习笔记
赛博算命系列:赛博算命
赛博算命之 ”梅花易数“ 的 “JAVA“ 实现 ——从玄学到科学的探索
赛博算卦之周易六十四卦JAVA实现:六幺算尽天下事,梅花化解天下苦。
[img]https
://i-blog.cs
dnimg.cn/direct/ff2158103de64f34b88205386c0e1d03.jpeg[/img]
[s
ize=5]#面向对象的介绍:[/s
ize]
面向:拿、找
对象:能干活的东西
面向对象编程:拿东西过来做对应的事情
面向对象编程的例子:
- import java.util.Random;
- import java.util.Scanner;
- public clas
- s
- mian {
- public s
- tatic void main(String[] args
- ) {
- //面向对象,导入一个随机数
- Random r = new Random();
- int data = r.nextInt(10)+1;
- //面向对象,输入一个随机数
- Sys
- tem.out.println(data);
- Scanner s
- c = new Scanner(Sys
- tem.in);
- // 面向对象,输出一个数
- Sys
- tem.out.println("请输入一个数:");
- int a = s
- c.nextInt();
- Sys
- tem.out.println(a);
- }
- }
复制代码 为什么java要接纳这种方法来编程呢?
我们在步调之中要干某种事,必要某种工具来完成,如许更符合人类的思维风俗,编程更简朴,更好明白。
面向对象的重点学习对象是什么?
学习获取已有对象并利用,学习如何自己计划对象并利用。——面向对象的语法
[s
ize=5]一、计划对象并利用[/s
ize]
[s
ize=4]1.类和对象[/s
ize]
[lis
t]
类(计划图):是对象共同特性的描述
[/lis
t] 如何定义类:
- public clas
- s
- 类名{
- 1.成员变量(代表属性,一般是名词)
- 2.成员方法(代表行为,一般是动词)
- 3.构造器(后面学习)
- 4.代码块(后面学习)
- 5.内部类(后面学习)
- }
复制代码- public clas
- s
- Phone{
- //属性(成员变量)
- String brand;
- double price;
- public void call(){
- }
- public void playGame(){
- }
- }
复制代码 如何得到对象?
- 如何得到类的对象:
- 类名 对象名= new 类名();
- Phone p = new Phone();
复制代码
[lis
t]
对象:是真实存在的详细东西
[/lis
t] 拿到对象后能做什么?
在JAVA中,必须先计划类,才得到对象
- public clas
- s
- phone {
- //属性
- String name;
- double price;
- public void call(){
- Sys
- tem.out.println("打电话");
- }
- public void s
- end(){
- Sys
- tem.out.println("发短信");
- }
- }
复制代码- //测试
- public clas
- s
- phoneTes
- t {
- public s
- tatic void main(String[] args
- ) {
- //创建手机对象
- phone p = new phone();
- //给手机对象赋值
- p.name = "小米";
- p.price = 1999;
- //获取手机对象的属性值
- Sys
- tem.out.println(p.name);
- Sys
- tem.out.println(p.price);
- //调用手机对象的方法
- p.call();
- p.s
- end();
- }
- }
复制代码 [s
ize=4]2.类的几个补充注意事项[/s
ize]
[lis
t]
用来描述一类事物的类,专业叫做:Javabean类。
在javabean类中,是不写main方法的。
在从前,编写main方法的类,叫做测试类。
我们可以在测试中创建javabean类的对象并举行赋值调用。
[/lis
t]- public clas
- s
- 类名 {
- 1.成员变量(代表属性)
- 2.成员方法(代表行为)
- }
复制代码- public clas
- s
- Student {
- //属性(成员变量)
- String name;
- int age;
- //行为方法
- public void s
- tudy(){
- Sys
- tem.out.println("好好学习,天天向上");
- }
- public void doHomework(){
- Sys
- tem.out.println("键盘敲烂,月薪过万");
- }
- }
复制代码
[lis
t]
类名首字母发起大写,必要见名知意,驼峰模式。
一个java文件中可以定义多个clas
s
类,且只能一个类是public修饰的类名必须成为代码文件名。
实际开辟中发起还是一个文件定义一个clas
s
类。
成员变量的完整定义格式是:修饰符 数据类型 变量名称=初始化值;一般无需指定初始化值,存在默认值。
- int age;
- //这里不写初始化值是因为,这里学生的年龄是一个群体的值,没有一个固定的初始化值。
- //如果给age赋值,比如是18岁,那就代表者所有的学生年龄都是18岁。
复制代码- //类的赋值不是在类里面赋值,而是在创建了对象之后再赋值,这时赋值的时这个特定的对象。
- Student s
- tu = new Student();
- Stu.name="张三";
- Stu.height=187;
复制代码 对象的成员变量的默认值规则
数据类型明细默认值根本类型byte,s
hort,int,long0根本类型float,double0.0根本类型booleanfals
e引用类型类、接口、数组、Stringnull- //编写女朋友类,创建女朋友类的对象,给女朋友的属性赋值并调用女朋友类中的方法。自己思考女朋友有哪些属性,有哪些行为?
- public clas
- s
- girlFriend {
- public s
- tatic void main(String[] args
- ) {
- //创建女朋友对象
- girl g = new girl();
- //给女朋友对象赋值
- g.name = "小红";
- g.age = 20;
- g.hobby = "唱歌";
- //获取女朋友对象的属性值
- Sys
- tem.out.println(g.name);
- Sys
- tem.out.println(g.age);
- Sys
- tem.out.println(g.hobby);
- //调用女朋友对象的方法
- g.eat();
- g.s
- leep();
- }
- }
复制代码- //这是一个类
- public clas
- s
- girl {
- //成员变量(代表属性)
- String name;
- int age;
- String hobby;
- //成员方法(代表行为)
- public void eat(){
- Sys
- tem.out.println("吃饭");
- }
- public void s
- leep(){
- Sys
- tem.out.println("睡觉");
- }
- }
复制代码 [/lis
t] [s
ize=4]3.开辟中类的计划[/s
ize]
先把需求拿过来,先要看这个需求当中有几类事物。每个事物,每类事务都要定义为单独的类,这类事物的名词都可以定义为属性,这类事物的功能,一般是动词,可以定义为行为。
[s
ize=5]二、封装[/s
ize]
[s
ize=4]1.封装的介绍[/s
ize]
封装是面向对象的三大特性:封装、继承、多态
封装的作用:告诉我们,如何精确计划对象的属性和方法。
- /**需求:定义一个类描述人
- 属性:姓名、年龄
- 行为:吃饭、睡觉*/
- public clas
- s
- Pers
- on{
- String name;
- int age;
- public void eat(){
- Sys
- tem.out.println("吃饭");
- }
- public void s
- leep(){
- Sys
- tem.out.println("睡觉");
- }
- }
复制代码 原则:对象代表什么,就得封装对应的数据,并提供数据对应的行为。
- public clas
- s
- Circle {
- double radius
- ;
- public void draw(){
- Sys
- tem.out.println("根据半径"+radius
- +"画圆");
- }
- }
- //人画圆,我们通常人为行为主体是人,其实是圆
- //例如:人关门,这个门一定是门自己关的,人只是给了作用力,是门自己关上的。
复制代码 [s
ize=4]2.封装的好处[/s
ize]
[lis
t]
对象代表什么,就得封装对应的数据,并提供数据对应的行为
降低我们的学习成本,可以少学,少记,或者说压根不用学,不用记对象有哪些方法,有必要时去找就行
[/lis
t] [s
ize=4]3.private关键字[/s
ize]
[lis
t]
是一个权限修饰符
可以修饰成员(成员变量和成员方法)
被private修饰的成员只能在本类中才气访问
- public clas
- s
- GirlFriend{
- private String name;
- private int age;
- private String gender;
- }
复制代码- public clas
- s
- leiMing {
- private int age;
- //s
- et(赋值)
- public void s
- etAge(int a){
- if(a<0||a>120){
- Sys
- tem.out.println("你给的年龄有误");
- return;
- }
- age = a;
- }
- //get(取值)
- public int getAge(){
- return age;
- }
- }
复制代码 针对private修饰的成员变量,如果必要被其他类利用,提供相应的利用
提供“s
etXxx(参数)”方法,用于给成员变量复制,方法用public修饰
提供“getXxx()”方法,用于获取成员变量的值,方法用public修饰
[/lis
t] 为什么要调用s
et和get呢?
封装是面向对象编程的四大特性之一,它将数据(成员变量)和利用数据的方法绑定在一起,并隐蔽对象的内部实现细节。通过将成员变量声明为 private,外部类无法直接访问和修改这些变量,只能通过类提供的 s
et 和 get 方法来间接利用。如许可以防止外部代码对数据举行非法或不适当的修改,保证数据的安全性和完整性。
[s
ize=5]三、this
关键字[/s
ize]
[s
ize=4]1.成员变量和局部变量[/s
ize]
- public clas
- s
- GirlFriend{
- private int age;//成员变量:方法的外面,类的里面
- public void method()
- {
- int age = 10;//局部变量:方法的里面
- Sys
- tem.out.println(age);
- }
- }
复制代码 成员变量和局部变量一致时,接纳就近原则
谁离我近,我就用谁
- public clas
- s
- GirlFriend{
- private int age;//成员变量:方法的外面,类的里面
- public void method()
- {
- int age = 10;//局部变量:方法的里面
- Sys
- tem.out.println(age);
- }
- }
- //在这里中,末了1个age间隔 age=10最近,所以末了一个age用的是10的值//假如我想用第一个int ,我们可以在Sys
- tem.out.println(this
- .age)age前加入:this
- . 这里就可以冲破就近原则,选择另一个变量
复制代码 在 Java 中,政府部变量(好比方法的参数)和类的成员变量重名时,就会产生定名辩说。在这种情况下,如果直接利用变量名,Java 默认会利用局部变量。而 this
关键字的一个紧张作用就是用来引用当前对象的成员变量,从而区分局部变量和成员变量。
[s
ize=4]2.举例[/s
ize]
下面通过一个简朴的示例来详细讲授从引用成员变量方向 this
关键字的用法:
- clas
- s
- Employee {
- // 定义成员变量
- private String name;
- private int age;
- // 构造方法,用于初始化员工信息
- public Employee(String name, int age) {
- // 这里参数名和成员变量名相同,使用 this
- 引用成员变量
- this
- .name = name;
- this
- .age = age;
- }
- // 设置员工姓名的方法
- public void s
- etName(String name) {
- // 使用 this
- 引用成员变量
- this
- .name = name;
- }
- // 获取员工姓名的方法
- public String getName() {
- return this
- .name;
- }
- // 设置员工年龄的方法
- public void s
- etAge(int age) {
- // 使用 this
- 引用成员变量
- this
- .age = age;
- }
- // 获取员工年龄的方法
- public int getAge() {
- return this
- .age;
- }
- // 显示员工信息的方法
- public void dis
- playInfo() {
- Sys
- tem.out.println("姓名: " + this
- .name + ", 年龄: " + this
- .age);
- }
- }
- public clas
- s
- This
- KeywordVariableExample {
- public s
- tatic void main(String[] args
- ) {
- // 创建一个 Employee 对象
- Employee employee = new Employee("李四", 25);
- // 调用 dis
- playInfo 方法显示员工信息
- employee.dis
- playInfo();
- // 调用 s
- etName 和 s
- etAge 方法修改员工信息
- employee.s
- etName("王五");
- employee.s
- etAge(30);
- // 再次调用 dis
- playInfo 方法显示修改后的员工信息
- employee.dis
- playInfo();
- }
- }
复制代码 [s
ize=3]代码详细表明:[/s
ize]
[s
ize=2]1. 类的成员变量定义[/s
ize]
- private String name;
- private int age;
复制代码 这里定义了两个私有成员变量 name 和 age,用于存储员工的姓名和年龄。
[s
ize=2]2. 构造方法中的 this
关键字利用[/s
ize]
- public Employee(String name, int age) {
- this
- .name = name;
- this
- .age = age;
- }
复制代码 在构造方法中,参数名 name 和 age 与类的成员变量名雷同。此时,this
.name 表示当前对象的成员变量 name,而直接利用的 name 则是构造方法的参数(局部变量)。通过 this
.name = name; 语句,将局部变量 name 的值赋给了当前对象的成员变量 name。同理,this
.age = age; 也是将局部变量 age 的值赋给了成员变量 age。
[s
ize=2]3. s
et 方法中的 this
关键字利用[/s
ize]
- public void s
- etName(String name) {
- this
- .name = name;
- }
- public void s
- etAge(int age) {
- this
- .age = age;
- }
复制代码 在 s
etName 和 s
etAge 方法中,同样存在参数名和成员变量名雷同的情况。利用 this
关键字来明确指定要利用的是当前对象的成员变量,制止了与局部变量的肴杂。
[s
ize=2]4. get 方法中的 this
关键字利用[/s
ize]
- public String getName() {
- return this
- .name;
- }
- public int getAge() {
- return this
- .age;
- }
复制代码 在 get 方法中,利用 this
.name 和 this
.age 来返回当前对象的成员变量的值。虽然在这种情况下,倒霉用 this
关键字也可以正常返回成员变量的值,因为这里没有局部变量与成员变量重名的题目,但利用 this
可以使代码的意图更加清晰,表明是在访问当前对象的成员变量。
[s
ize=2]5. dis
playInfo 方法中的 this
关键字利用[/s
ize]
- public void dis
- playInfo() {
- Sys
- tem.out.println("姓名: " + this
- .name + ", 年龄: " + this
- .age);
- }
复制代码 在 dis
playInfo 方法中,利用 this
.name 和 this
.age 来获取当前对象的成员变量的值,并将其输出。
[s
ize=5]四、构造方法[/s
ize]
[s
ize=4]1.构造方法的概述[/s
ize]
构造方法也叫做构造器、构造函数
[s
ize=4]2.构造方法的格式[/s
ize]
- public clas
- s
- Student{
- 修饰符 类名(参数){
- 方法体;
- }
- }
复制代码- public clas
- s
- Student {
- private String name;
- private int age;
- //如果我们自己没有写构造方法
- // 那么编译器会自动生成一个无参构造方法
- public Student() {
- Sys
- tem.out.println("无参构造方法");
- }
- public Student(String name, int age) {
- this
- .name = name;
- this
- .age = age; //有参构造方法
- }
- public String getName() {
- return name;
- }
- public void s
- etName(String name) {
- this
- .name = name;
- }
- public int getAge() {
- return age;
- }
- public void s
- etAge(int age) {
- this
- .age = age;
- }
- }
复制代码- public clas
- s
- StudentTes
- t {
- public s
- tatic void main(String[] args
- ) {
- //创建类的对象
- //调用的空参构造
- //Student s
- 1 = new Student();
- Student s
- = new Student(name:"张三", age:20);
- Sys
- tem.out.println(s
- .getName());
- Sys
- tem.out.println(s
- .getAge());
- }
- }
复制代码 特点:
[lis
t=1]
方法名与类名雷同,大小写也要一致
没有返回值类型,连void都没有
没有详细的返回值(不能由return带回效果数据)
[/lis
t] 执行机遇:
[lis
t=1]
创建对象的时候由捏造机调用,不能手动调用构造方法
每创建一次对象,就会调用过一次构造方法
[/lis
t] [s
ize=4]3.构造方法的作用[/s
ize]
在创建对象的时候,由捏造机主动调用构造方法,作用是给成员变量举行初始化的
[s
ize=4]4.构造方法的分类[/s
ize]
- public clas
- s
- Student{
- private String name;
- private int age;
-
- public Student(){
- ...//空参构造方法
- }
-
- public Student (String name, int age){
- ....//带全部参数构造方法
- }
- }
复制代码 无参构造方法:初始化的对象时,成员变量的数据均接纳默认值
有参构造方法:在初始化对象的时候,同时可以为对象举行
[s
ize=4]5.构造方法的注意事项[/s
ize]
[lis
t=1]
构造方法的定义
[lis
t]
如果没有定义构造方法,体系将给出一个默认的无参数构造方法
如果定义了构造方法,体系将不再提供默认的构造方法
[/lis
t]
构造方法的重载
[lis
t]
带参构造方法,和无参构造方法,两者方法名雷同,但是参数差别,这叫做构造方法的重载
[/lis
t]
推荐的利用方式
[lis
t]
无论是否利用,都动手书写无参数构造方法,和带全部参数的构造方法
[/lis
t]
[/lis
t] [s
ize=5]五、标准JavaBean[/s
ize]
[s
ize=4]1.标准的JavaBean类[/s
ize]
[lis
t=1]
类名必要见名知意
成员变量利用private修饰
提供至少两个构造方法
[lis
t]
无参构造方法
带全部参数的构造方法
[/lis
t]
成员方法
[lis
t]
提供每一个成员变量对应的s
etXxx()/getXxx()
如果另有其他行为,也必要写上
[/lis
t]
[/lis
t]- 我们再写一个javabean中会遇到一个问题:
- 这样写纯体力活啊!没事的没事的!我们有快捷键:
-
- 方法一:
-
- alt+ins
- ert 或 alt+ins
- ert+Fn
- alt+ins
- ert 第一个是构造函数,点击无选择生成的是空参 ,全选ok生成的是有参数的构造函数
- alt+ins
- ert 点击s
- etter和geteer,全选生成的是s
- et和get
-
- 方法二:
-
- 下载插件pdg,下载完成后点击空白处就会出现。然后点击Ptg To JavaBean
复制代码 [s
ize=5]六、对象内存图[/s
ize]
[s
ize=4]1.一个对象的内存图[/s
ize]
- Student s
- = new Student();
复制代码 [lis
t=1]
加载clas
s
文件
说明局部变量
在堆中开辟一个空间
默认初始化
显示初始化
构造方法初始化
将堆中空间的地点值赋值给左边的局部变量
[/lis
t]- 举例:
- public clas
- s
- Student{
- String name;
- int age;
-
- public void s
- tudy(){
- Sys
- tem.out.println("好好学习")
- }
- }
复制代码- public clas
- s
- Tes
- tStudent{
- public s
- tatic void main(String [] args
- ){
- Student s
- = new Student();
- Sys
- tem.out.println(s
- );
- Sys
- tem.out.println(s
- .name+"...."+s
- .age);
- s
- .name = "阿强";
- s
- .age = 23;
- Sys
- tem.out.println(s
- .name+"..."+s
- .age);
- s
- .s
- tudy();
- }
- }
复制代码[img]https
://i-blog.cs
dnimg.cn/direct/41b63ac369a140eead10b89da9c0486d.png[/img]
剖析:
[s
ize=3]内存执行顺序剖析(基于Java内存模型)[/s
ize]
[s
ize=2]1. 类加载阶段(方法区)[/s
ize]
[lis
t]
加载clas
s
文件
JVM将
和
加载到方法区,存储类结构信息(字段、方法署名、常量池等)。
[lis
t]
Student类包罗字段name(String)、age(int)和方法s
tudy()。
Tes
tStudent类包罗main()方法入口。
[/lis
t]
[/lis
t] [s
ize=2]2. 栈内存利用(main方法启动)[/s
ize]
[lis
t]
声明局部变量
执行main()时,在栈内存中创建main方法的栈帧,声明局部变量s
(此时s
未指向任何对象,值为null)。
[/lis
t] [s
ize=2]3. 堆内存分配(对象实例化)[/s
ize]
[lis
t]
在堆中开辟空间
执行new Student()时,在堆内存中为Student对象分配空间,内存大小由字段类型决定(String引用 + int值)。
[/lis
t] [s
ize=2]4. 对象初始化流程[/s
ize]
[lis
t]
默认初始化
对象字段赋默认值:
[lis
t]
name → null(引用类型默认值)
age → 0(根本类型默认值)。
[/lis
t]
显示初始化(本例中无)
如果类中字段有显式赋值(如String name = "默认";),此时会执行。但示例代码未定义,此步调跳过。
构造方法初始化(本例中无)
如果存在构造方法(如public Student() { age = 18; }),会通过构造器赋值。示例代码未定义构造方法,此步调跳过。
[/lis
t] [s
ize=2]5. 变量关联与利用[/s
ize]
[lis
t]
地点赋值给局部变量
堆中对象地点赋值给栈帧中的
变量,完成引用关联。
[lis
t]
执行Student s
= new Student();
后,s
指向堆内存中的对象。
[/lis
t]
对象字段修改
后续代码通过s
.name = "阿强";和s
.age = 23;直接修改堆中对象的字段值,无需重新初始化。
[/lis
t] [s
ize=2]6. 方法调用(方法区与栈协作)[/s
ize]
[lis
t]
执行s
.s
tudy()
[lis
t]
从方法区加载s
tudy()的字节码指令。
在栈中创建s
tudy()方法的栈帧,执行Sys
tem.out.println(" 好好学习")(注:用户代码此处缺少分号,实际会编译报错)。
[/lis
t]
[/lis
t] [s
ize=3]内存利用完整流程总结[/s
ize]
步调利用内容内存区域示例代码表现1加载类信息方法区Student和Tes
tStudent类加载2声明局部变量s
栈内存Student s
;3堆中分配对象空间堆内存new Student()4字段默认初始化(null/0)堆内存s
.name 和s
.age 初始值5显式/构造初始化(无)-代码未定义相关逻辑6对象地点赋值给s
栈内存s
= new Student();7修改字段值堆内存s
.name = "阿强";等利用 [s
ize=3]关键现象表明[/s
ize]
[lis
t]
Sys
tem.out.println(s
) 输出哈希值
因打印对象时默认调用toString(),而Student未重写该方法,输出格式为类名@哈希值。
字段值修改的可见性
直接通过引用s
修改堆中对象字段,全部指向该对象的引用均会看到更新后的值。
编译隐患
s
tudy()方法中Sys
tem.out.println(" 好好学习")缺少分号,实际运行前会因语法错误中断。
[/lis
t] [s
ize=4]2.多个对象的内存图[/s
ize]
- 举例:public clas
- s
- Student{ String name; int age; public void s
- tudy(){ Sys
- tem.out.println("好好学习"); }}
复制代码- public clas
- s
- Tes
- tStudent{ public s
- tatic void main(String [] args
- ){ Student s
- = new Student(); Sys
- tem.out.println(s
- ); s
- .name = "阿强"; s
- .age = 23; Sys
- tem.out.println(s
- .name+"..."+s
- .age); s
- .s
- tudy(); Student s
- 2= new Student(); Sys
- tem.out.println(s
- 2); s
- 2.name = "阿珍"; s
- 2.age = 24; Sys
- tem.out.println(s
- 2.name+"..."+s
- 2.age); s
- 2.s
- tudy(); }}
复制代码[img]https
://i-blog.cs
dnimg.cn/direct/46b71a4948d14bc2888a383e5437901e.png[/img]
第二次在创建对象时。clas
s
文件不必要再加载一次
[img]https
://i-blog.cs
dnimg.cn/direct/ef6c55232ff247d79c47926fb799ef8c.png[/img]
剖析:
[s
ize=3]2.1、执行顺序与内存利用分步剖析[/s
ize]
[s
ize=2]1. 加载clas
s
文件(方法区)[/s
ize]
[lis
t]
触发条件:首次利用Student类时。
利用内容:
[lis
t]
将Student.clas
s
加载到方法区,存储类结构(字段name、age和方法s
tudy()的定义)。
将Tes
tStudent.clas
s
加载到方法区,存储main()方法入口。
[/lis
t]
[/lis
t] [s
ize=2]2. 声明局部变量(栈内存)[/s
ize]
[lis
t]
利用内容:
[lis
t]
执行main()方法时,在栈内存创建main方法的栈帧。
声明局部变量s
和s
2(初始值均为null)。
[/lis
t]
[/lis
t] [s
ize=2]3. 堆内存分配(对象空间开辟)[/s
ize]
[lis
t]
利用内容:
[lis
t]
new Student()触发堆内存分配,根据类结构盘算对象大小(String引用 + int值)。
示例:
[lis
t]
s
= new Student() → 堆地点0x001。
s
2 = new Student() → 堆地点0x002(独立空间)。
[/lis
t]
[/lis
t]
[/lis
t] [s
ize=2]4. 默认初始化(堆内存)[/s
ize]
[lis
t]
利用内容:
[lis
t]
对象字段赋默认值:
[lis
t]
name → null(引用类型默认值)。
age → 0(根本类型默认值)。
[/lis
t]
示例:
[lis
t]
s
的初始状态:name=null, age=0。
s
2的初始状态:name=null, age=0。
[/lis
t]
[/lis
t]
[/lis
t] [s
ize=2]5. 显示初始化(堆内存)[/s
ize]
[lis
t]
触发条件:类中显式赋值的字段(如String name = "默认")。
当前代码:
[lis
t]
Student类未定义显式赋值字段,跳过此步调。
[/lis
t]
[/lis
t] [s
ize=2]6. 构造方法初始化(堆内存)[/s
ize]
[lis
t]
触发条件:存在自定义构造方法(如public Student() { ... })。
当前代码:
[lis
t]
Student类未定义构造方法,利用默认无参构造器,跳过此步调。
[/lis
t]
[/lis
t] [s
ize=2]7. 地点赋值(栈内存)[/s
ize]
[lis
t]
利用内容:
[lis
t]
将堆内存地点赋值给栈中的局部变量。
示例:
[lis
t]
s
= 0x001(指向第一个对象)。
s
2 = 0x002(指向第二个对象)。
[/lis
t]
[/lis
t]
[/lis
t] [s
ize=3]2.2、内存模型与对象独立性的关键验证[/s
ize]
[s
ize=2]1. 对象独立性的表现[/s
ize]
对象堆地点字段修改后的值s
0x001name="阿强", age=23s
20x002name="阿珍", age=24
[lis
t]
验证逻辑:
[lis
t]
s
和s
2指向差别堆地点,修改此中一个对象的字段不会影响另一个对象。
Sys
tem.out.println(s
== s
2) → 输出fals
e。
[/lis
t]
[/lis
t] [s
ize=2]2. 内存利用流程图解[/s
ize]
[img]https
://i-blog.cs
dnimg.cn/direct/8f2ce66e5d91417e99ca6669b98253ea.png[/img]
[s
ize=3]2.3、执行流程总结(分阶段)[/s
ize]
阶段利用内容内存区域类加载加载Student和Tes
tStudent类信息方法区栈帧创建声明s
和s
2(初始null)栈内存堆内存分配为s
和s
2分配独立空间堆内存对象初始化默认初始化 → 显式赋值(用户代码修改)堆内存方法调用s
tudy()从方法区加载逻辑到栈执行栈内存 [s
ize=3]2.4、常见题目解答[/s
ize]
[s
ize=2]1. 为什么Sys
tem.out.println(s
) 输出地点?[/s
ize]
[lis
t]
缘故原由:未重写toString()方法,默认调用Object.toString() ,格式为类名@哈希值。
[/lis
t] [s
ize=2]2. 显示初始化和构造方法初始化有何区别?[/s
ize]
[lis
t]
显示初始化:直接在类中赋值字段(如String name = "张三"),编译时主动插入到构造方法中。
构造方法初始化:通过自定义构造器赋值(优先级高于显示初始化)。
[/lis
t] [s
ize=2]3. 如何优化内存利用?[/s
ize]
[lis
t]
复用对象:制止频繁new对象(尤其循环中)。
垃圾回收:main()竣事后,s
和s
2成为垃圾对象,由GC主动回收。
[/lis
t] 附:修正后的代码输出示例
- Student@1b6d3586
- 阿强...23
- 好好学习
- Student@4554617c
- 阿珍...24
- 好好学习
复制代码 [s
ize=4]3.两个变量指向同一个对象内存图[/s
ize]
- 举例:public clas
- s
- Student{ String name; int age; public void s
- tudy(){ Sys
- tem.out.println("好好学习"); }}
复制代码- public clas
- s
- Tes
- tStudent{ public s
- tatic void main(String [] args
- ){ Student s
- = new Student(); s
- .name = "阿强"; Student s
- 2= s
- ; s
- 2.name = "阿珍"; Sys
- tem.out.println(s
- .name+"..."+s
- 2.name); }}
复制代码[img]https
://i-blog.cs
dnimg.cn/direct/05fd25620d884648977f6e5cd3b29ee5.png[/img]
[s
ize=3]3.1、类加载阶段(方法区)[/s
ize]
[lis
t=1]
加载Tes
tStudent.clas
s
当JVM启动时,起首将Tes
tStudent.clas
s
加载到方法区,存储类结构信息(成员方法、字段描述等)
加载Student.clas
s
执行new Student()时触发类加载机制,将Student.clas
s
加载到方法区,包罗name、age字段和s
tudy()方法元数据
[/lis
t] [s
ize=3]3.2、栈内存利用(main方法栈帧)[/s
ize]
[lis
t=1]
声明局部变量
在main方法栈帧中创建引用变量s
(地点未初始化)和s
2(此时两者均为null)
对象创建指令
new Student()利用码触发堆内存分配,此时:
[lis
t]
在堆中生成对象内存空间(包罗对象头 + String name + int age)
默认初始化:name=null,age=0(根本类型和引用类型的零值初始化)
显式初始化:由于Student类没有直接赋值的字段(如String name = "默认名"),此阶段跳过
[/lis
t]
构造方法执行
若存在构造方法(本案例未定义),会通过invokes
pecial指令调用<init>方法完成初始化
[/lis
t] [s
ize=3]3.3、堆内存利用(对象关联)[/s
ize]
[lis
t=1]
地点赋值
将堆中Student对象地点赋值给栈帧中的s
变量(完成s
= new Student())
引用通报
s
2 = s
利用使s
2指向堆中同一个对象(此时两个引用共享对象数据)
字段修改
通过s
2.name = "阿珍"修改堆内存对象数据,此时s
.name 同步变化(引用指向同一实体)
[/lis
t] [s
ize=3]3.4、终极内存结构[/s
ize]
内存区域存储内容方法区Tes
tStudent类字节码、Student类元数据(包罗s
tudy()方法代码)堆内存Student对象实例(name=“阿珍”, age=0)栈内存main方法栈帧:s
=0x100(指向堆对象), s
2=0x100(与s
同地点) [s
ize=3]3.5、输出效果分析[/s
ize]
Sys
tem.out.println(s
.name+"..."+s
2.name)
→ 输出阿珍...阿珍(s
与s
2引用同一对象,堆内数据修改对全部引用可见)
关键明白点:引用类型变量的赋值利用通报的是对象地点值,而非创建新对象。这种特性是Java对象共享机制的核心表现。
[s
ize=4]4.this
的内存原理[/s
ize]
- public clas
- s
- Student{ private int age; public void method()
- { int age=10; Sys
- tem.out.println(age);//10 Sys
- tem.out.println(this
- .age);//成员变量的值 0 }}
复制代码 this
的作用:区分局部变量和成员变量
this
的本质:地点方法调用者的地点值
- public clas
- s
- Student{ private int age; public void method()
- { int age=10; Sys
- tem.out.println(age);//10 Sys
- tem.out.println(this
- .age);//成员变量的值 0 }}
复制代码- public clas
- s
- StudentTes
- t{ public s
- tatic void main (String[] args
- ){ Student s
- = new Student();
- s
- .method()
- ; }}
复制代码[img]https
://i-blog.cs
dnimg.cn/direct/d1c8c2dd1424426b8d6534cd5707540f.png[/img]
[s
ize=3]4.1、类加载阶段(方法区核心利用)[/s
ize]
[lis
t=1]
加载StudentTes
t.clas
s
[lis
t]
JVM启动时优先加载含main()的类到方法区
存储类元数据:静态变量、方法表(含main()入口地点)
[/lis
t]
触发Student.clas
s
加载
[lis
t]
当执行new Student()时触发类加载
方法区新增:
[lis
t]
字段描述表(private int age的访问权限和偏移量)
method()
的字节码指令集合
隐式默认构造器<init>方法(因无自定义构造方法)
[/lis
t]
[/lis
t]
[/lis
t] [s
ize=3]4.2、对象实例化流程(堆栈协同)[/s
ize]
步调内存区域详细行为代码对应3栈内存在main方法栈帧声明局部变量s
(初始值null)Student s
;4堆内存分配对象空间:对象头(12字节)+ int age(4字节)= 16字节new Student()5堆内存默认初始化:age=0(根本类型零值添补)隐式执行6堆内存构造方法初始化:执行空参数的<init>方法(无实际利用)隐式调用7栈内存将堆地点(如0x7a3f)赋值给s
变量s
= new... [s
ize=3]4.3、方法调用时的内存隔离(栈帧作用域)[/s
ize]
执行s
.method()
时发生:
[lis
t=1]
新建栈帧:在栈顶创建
的独立空间,包罗:
[lis
t]
隐式参数this
(指向堆地点0x7a3f)
局部变量age=10(存储于栈帧变量表)
[/lis
t]
变量访问规则:
输出语句内存访问路径效果Sys
tem.out.println(age)访问栈帧局部变量表10Sys
tem.out.println(this
.age)通过this
指针访问堆内存字段0
[/lis
t] [s
ize=3]4.4、关键差异对比表[/s
ize]
特性成员变量this
.age局部变量age存储位置堆内存对象内部栈帧局部变量表生命周期与对象共存亡随方法栈帧销毁而消失初始化值默认零值(int=0)必须显式赋值访问方式需通过对象引用直接访问 [s
ize=3]4.5、技能扩展:this
的底层实现[/s
ize]
当调用method()
时:
[lis
t=1]
字节码层面:
- java复制aload_0 // 将this
- 引用压入利用数栈(对应堆地点0x7a3f)getfield #2 // 根据字段偏移量读取堆中age值(#2为字段符号引用)
复制代码 内存隔离机制:局部变量age会遮蔽同名的成员变量,必须通过this
.显式穿透访问堆数据
[/lis
t] [s
ize=4]5.根本数据类型和引用数据类型的区别[/s
ize]
[s
ize=3]5.1根本数据类型[/s
ize]
- public clas
- s
- Tes
- t{ public s
- tatic void main (String [] args
- ){ int a = 10; }}
复制代码 根本数据类型:
在变量当中存储的是真实的数据值
从内存角度:数据值是存储再自己的空间中
特点:赋值给其他变量,也是赋值的真实的值。
[s
ize=3]5.2引用数据类型[/s
ize]
- public clas
- s
- Tes
- tStudent{ public s
- tatic void main(String[] args
- ){ Student s
- =new Student; }}
复制代码 引用数据类型:
堆中存储的数据类型,也就是new出来的,变量中存储的是地点值。引用:就是利用其他空间中数据的意思。
从内存的角度:
数据值是存储在其他空间中,自己空间中存储的是地点值
特点:
赋值给其他变量,赋的地点值。
[s
ize=5]七、补充知识:成员变量、局部变量区别[/s
ize]
成员变量:类中方法外的变量
局部变量:方法中的变量
[img]https
://i-blog.cs
dnimg.cn/direct/04b9e9df56954b67b2967128af01fa0c.png[/img]
区别:
区别成员变量局部变量类中位置差别类中,方法外方法内、方法说明上初始化值差别有默认初始化值没有,利用前必要完成赋值内存位置差别堆内存栈内存生命周期差别随着对象的创建而存在,随着对象的消失而消失随着方法的调用而存在,随着方法的运行竣事而消失作用域整个类中有效当火线法中有效 [img]https
://i-blog.cs
dnimg.cn/direct/bc48af1d726145168d00ca3c616ed6e4.jpeg[/img]
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |