前言
提示:开始思考了:
在第一课的内容中,我们初次接触了编程的内容,但是目前编程的状态还只是停留在面向过程编程。在前面做的示例里,如果我们要将整个系统的功能完善丰富起来,代码量会越来越多。如果全部把它写在同一个代码块里面,不是说不可以。一方面是臃肿和混乱,一方面不方便你查找(在代码越来越多的时候)。
在引入篇我们提到过,java是面向对象的编程语言。这里的对象不单是指你的男女朋友。是可以对一切能够想象的到的对象进行抽象设计,这在java中称之为类(也就是在上篇文章中使用的.java
文件,文件内部需要用public class 类名称{}进行标识
,类名称和文件名称要一致)。一个类通常被设计为以最核心(最符合你要抽象的那个现实对象的能力范围)。比如唱、跳、rap、打篮球,这些都属于蔡徐坤的能力范围。
当下很流行的I(Introverted 内向的)人,E(Extraverted 外向的)人。我们也可以设计为一类(如果程序有这个需要这需要你自己判断)。比如蔡徐坤我们在程序的抽象中除了他本身的唱跳rap打篮球之外,还可以(可能、或许)
为他进行I、E人的归类(或者其它任何具有意义的归类)。这在程序中叫做继承(但是我更建议你暂时去理解为:对具备某种相同能力或特征的归类)。
设计类时应该思考的方向:
1.比如歌手这一类应该设计为所有歌手都具备的(打篮球就不是歌手必须具备的虽然他两确实好像都会)。
2.所以设计类时周杰伦和蔡徐坤都在程序设计中
继承歌手
这一类所以可以抽象出歌手共同的能力-唱歌(歌手类)
来让他两(周类、蔡类)去继承。
3.周杰伦和蔡徐坤具备的能力肯定是不同的,比如周杰伦作曲编曲钢琴吉他等等,蔡徐坤可能更专注娱乐领域和唱跳rap打篮球等等。(所以某个类
特有的能力不具备共通性的(能力:他们都会唱歌),特征:他们头发都很长
时,就不适合去设为歌手
必备的能力)
4.有了继承
后,歌手类
在java中称之为父类,周类、蔡类
为子类。
类继承的基础意义:
1.避免重复造轮子
(尽量少写重复的代码):如果你程序里面需要涉及到成千上万个歌手(类)时,对于相同的歌手必备的唱歌功能你要每一个类都实现一遍(相关的代码),(因为写重复的内容对于新手而言便于练习记忆,前面提到过不同阶段干什么事情,熟练之后就显得非常不必要了)。
2.程序运行时的多态
(多种状态的运行),试想你是一家公司的老板(你有一个秘书和几百个员工),当你需要你的某个员工来给你提交工作报告。你不需要自己一个个去找,你只需要通知你得秘书一个人
就可以将要求传达到任何一位员工。这里这个秘书就是多态能力
体现的一种。程序和现实生活中一样,程序是死的(事情是死的),但是你得让你的代码活起来(办法是人想出来的)。
3.封装
&函数
:试想一下你购买的快递,卖家将你想要的东西封装起来,到你手上打开就能得到你想要的东西它是一个具备某种完整性的产品
。封装就是实现一个具备完整性的功能(执行、调用它就能够得到某种效果)
,比如周杰伦唱歌的功能就可以作为一个能力的封装
,打篮球又可以做另一个能力的封装。要注意的是它们需要分开封装(不同的函数),因为封装需要具备功能单一性
。因为只有这样才能在你想听周杰伦唱歌但并想看他打篮球时,只调用他唱歌的函数。
4.其它例子的引发思考(类的设计最终会达到一个最底端,也就从抽象到不能再被分化的某个类):
提示:开始实践了
一、函数
函数与类在语法上的区别
package Lesson1;
import java.util.Scanner;
public class Grammar {
public static void main(String[] args) {
}
}
一个类的语法是只有一对
{}
并且以public class
开头,比如示例中的public class Grammar {}
,一个类的所有能力(封装的函数)需要写在类的{}
中间,以标识为能力或特征的开始和结束。
一个函数的语法则要多出一对()
,而(String[] args)
这个的用处叫参数
也叫入(输入进来的)参
,一个函数的能力还需要被一对{}
来标识能力的开始和结束。
函数的声明(定义 or 设计)与调用(执行)与执行结果的处理
函数的声明(定义)
这里需要思考的是,既然对于某种能力的封装
,那么就需要考虑它需不需要和外界
交流,也就是入参
,交流之后的结果是在函数内完成,还是需要继续和外界
交流(返回结果?),怎么做呢?试想你需要封装一个函数,它的功能是要计算你输入进来参数之和,那就需要入参比如:你要计算什么?(和外界有交流了,因为函数本身的功能是将不同的数字相加,至于要相加什么内容函数并不知道),【计算】,相加后的处理结果怎么处理,是在函数内部处理还是需要和函数外界进一步交流
。
public void increase (int a,int b) { int c = a + b ; System.out.println(c); }
当你需要
接收
一个输入
时,我们需要定义我们的类型
,也就是我们期望它大概是什么样子
。用户输入的或函数参数的输入。在这个功能里我们需要将两个整数相加。而后输出它们相加的结果变量c
。
定义类时相应public
之后的class
,而函数(方法)这里void
是什么?如果你的需求不是输出这个相加的结果而只是得到
这个函数计算的结果。那么这个函数需要具备返回结果
的功能,这个时候应该是:将void替换为你需要返回的样子(类型 比如int
)public int increase (int a,int b) { int c = a + b ; return c; }
函数的调用
还记得第一课中接收
用户的输入结果
时怎么写的么。customerInput = scanner.nextInt();
,目前可以把这句代码解释为:使用了scanner这个类
所封装
的nextInt()
函数(方法)。那么我们刚才写的increase
函数应该使用int d = increase(1,2);
的方式来进行调用(执行)
。
需要注意的是在scanner.nextInt();
调用时,则是调用别的类(scanner)
所封装的函数
,并且这个函数没有入参(不需要输入值来完成功能)
而刚才我们做的increase
函数则需要通过用户的输入然后得到结果。
当在当前类(【语法】一个类的{}
范围之类)函数之间相互调用时,不需要import java.util.Scanner;
,不需要Scanner scanner = new Scanner(System.in);
,也不需要scanner.nextInt();
。
函数执行结果的处理
void?int?return?:void(字面意思
无用
:表示没有返回
),将void 替换为 int
时则表示又返回,并且是int(整数字型的返回内容),替换后还需用return
关键字来返回具体的整型内容(变量a和b的相加结果c)
,c也是整型的,所以定义了什么返回类型就一定要相同的类型返回,比如:
1.没有相应返回
2.返回错误的类型
可以发现1.1是double(带小数点的数字类型)时,和int不对称
,所以工具提示语法错误
。
customerInput = scanner.nextInt();
与int d = increase(1,2);
的区别:
1.根据前面示例的:customerInput
的int整型类型的变量
已经在函数的前部分
定义过了,不需要重复声明或者可以理解为强调int
,并且前面提到过在相同代码快内不能重复声明相同名字
的变量
。
2.int d = increase(1,2);
演示的则是在该代码快
还没有声明 or 定义
过。在相同代码块(和被嵌套
的子代码块内
)是不能重复声明的。比如我们在main函数的{}
内声明了customerInput
,然后又在main函数内的for(){}代码块重复声明时
出现了熟悉的红色波浪线(IDEA工具提示的)表示语法错误。
二、类
1.类的定义
首先每个类在计算机文件中的体现都是:xxx.java,xxx就是类名,也是.java文件里声明的 public class xxx {}。创建方法有几种:
1.使用IDEA工具视图中在
src
包下面的任意包
中右击(为什么是src
下的包(打开磁盘管理时看到的其实就是文件夹)前面提到过,IDEA工具只会帮你管理src
下的包和类。)
2.直接在文件管理器中找到相应的包路径下
新建。
3.直接复制,比如在开源社区中你觉得比较有意思的实现,复制到你的路径中来。
其次需要知道的类名称如何写,首先它应当(也必须)起到你看到一个类的类名时能给你最直观的感受(它是干嘛的)
,如果你给它叫ABCDEFG。并不是说不可以,只是随着时间和项目的进展类会越来越多。届时你将很难回忆起它是干什么的,需要花更多的时间来研究。
约定的命名方式:java以骆驼命名法
来给类起名字,比如流行歌手->public class PopSinger {}
,两个单词组成,不同单词的首字母大写,中间无空格无特殊符号({} [] () . ;
)等,目前你记住纯英文就对了。而函数则是以整个组合的首字母小写来命名比如:public void popSinger() {}
。变量名也是如此:int popSinger = 0;
。
2.类的使用
2.1 不同包下的类互相引用
还记得前面使用过的import java.util.Scanner;
吗?如果是在不同包
下的类时,则需要通过提前引入
的方式。因为JAVA也需要明白,你使用的这个类从何而来,否则它无法运行。
为了方便你的记忆我将在此课程使用拼音组合来给类命名(这是在实际项目中比较忌讳的,在这里只是方便你在
入门混乱时的记忆
)
我Lesson2中新建一个关于计算
的类,同时为它封装了一个做加法的函数:package Lesson2;//这里是 /src/Lesson2 public class JiSuan { //通过入参int a,int b做加法,并且返回它们的结果值。 public int increase (int a,int b) { int c = a + b ; return c; } }
在Lesson1中新建一个用于测试演示
调用类
的类:package Lesson1;//这里是 /src/Lesson1 import Lesson2.JiSuan; public class CeShiJiSuan { public static void main(String[] args) { JiSuan jiSuan = new JiSuan(); int d = jiSuan.increase(1,2); System.out.println(d); } }
2.2 不同包下的类互相调用
前面我们提到过,JAVA会以
包名(Lesson1或Lesson2).类名(JiSuan或CeShiJiSuan)
作为真正的类名。同时你也可以自己试试(多尝试,不要钱)在同一个包/不同的包下新建相同的类、类名一样的效果。
同时需要注意的是我们在Lesson1包下的CeShiJiSuan
类中调用了Lesson2包下的JiSuan
类的increase
函数并且输入了1和2两个参数,用,
分开,并且定义了变量int d
来接收该函数的返回值
,并将其输出到命令行(控制台)。所以调用一个其它类的方式是:
1.使用import
导入其它包下的类(相同包下的类调用自己尝试吧)
2.使用JiSuan jiSuan = new JiSuan();
来声明一个名字叫jiSuan
的,类型为JiSuan(类)
的变量
。和定义整数值时的含义是一样,int(类型,有语法限定)d(变量名,自由组合部分特殊字符不能使用以外,这个可以自己慢慢尝试,报错了就是不行)
,JiSuan(类型,必须存在这个类,无论是你自己工程中有的,还是引入的第三方的,还是java自带的) jiSuan(变量名,和d一样)
。
3.调用(执行)
时,是通过jiSuan声明的的变量名.increase函数名(以及有无入参)
,然后该函数是否有返回值。例如:int d = jiSuan.increase(1,2);
调用时,你是可以选择不接收该函数的返回值的(jiSuan.increase(1,2);
),同时如果该函数没有声明返回如果你尝试去接收它的返回值就会报错
。
2.3 new?对象?
在现实情况中,很我们即使抽象到了不能再细化的程度时,它仍然具备其它不同特征:
- 虽然是相同的锤子、钳子、剪子,但是不可能全人类一起用一把。虽然锤子也是
一类
。 - 也就是说,锤子无论在
你的程序
还是现实生活中
它都只是一个模型
。 - 这个模型需要反复不断地被使用,被具体为真正的
对象
。它们拥有不同的生产日期、不同的买家、不同的使用历程。 - 在代码中我们抽象出了
商品Goods类
,并且给他们贴上了不同的生产日期标签。 - 而后通过
Goods g1 = new Goods();
,Goods g2 = new Goods();
不同的变量名称
、通过new
的方式生成了两个同类都是Goods
但是不同对象的g1
,g2
。
package Lesson2;
public class Goods {
String proDate;
}
package Lesson2;
public class GoodsTest {
public static void main(String[] args) {
Goods g1 = new Goods();
g1.proDate = "2022-2-2";
Goods g2 = new Goods();
g2.proDate = "2033-3-3";
System.out.println(g1.proDate);
System.out.println(g2.proDate);
}
}
注意代码中的 proDate
变量
和属性
说法上同意。比如上面示例中它在代码中被设计为商品
生产日期属性(实际是String类型的变量)
,在java代码中任意类型都可以被定义为某个类中的属性
。属性可以在类public class Goods{}
代码块中定义,也可以在函数
中定义。
- 类代码块中定义的属性可以在函数中重复定义,因为类代码块中定义的变量,该类中的
所有函数
都可以使用它。而某个函数中定义的变量其它函数不可以使用。 - 函数代码块中
不能
重复定义相同的变量名。 - 类代码块中
也
不能重复定义相同的变量名。
2.4 什么是静态函数(方法)
静态函数就是不需要类似JiSuan jiSuan = new JiSuan();
这样的声明就可以直接使用(调用执行)的。但是他们也同时需要引入(无论如何java都需要知道你这个类是从哪里来的,否则它将无法识别)。
1.Lesson2包下的JiSuan类中声明一个静态方法ShuChuJiSuanJieGuo,入参是一个String(字符串)类型的入参:
package Lesson2;
public class JiSuan {
public static void ShuChuJiSuanJieGuo(String a) {
System.out.println(a);
}
}
2.在Lesson1包下的CeShiJiSuan类中通过JiSuan.ShuChuJiSuanJieGuo("");
的方式调用该函数,不需要声明。
package Lesson1;
import Lesson2.JiSuan;
public class CeShiJiSuan {
public static void main(String[] args) {
JiSuan.ShuChuJiSuanJieGuo("字符串类型的入参内容234");
}
}
需要对比静态方法和非静态方法的定义语法
和调用语法
。一个是public static void 或 public static int(静态有返回值)
,一个是public void 或 public int(非静态有返回值)
,入参的定义语法是一样的。目前为止,你只需要知道这个区别即可。
2.5 类中main函数
所有的类下都可以定义main函数,也就是说你得程序可以从任意类中的public static void main(String[] args) {}
开始执行,它也是程序主入口。俗称;程序主入口
。而其中的String[] args
入参,也是必要的。你可以尝试删除它,main函数的名字将在idea中不再高亮。也意味着它失去了程序主入口的功能。
只有在有该函数的情况下,才有在idea中右击选择运行的功能。
package Lesson1;
import Lesson2.JiSuan;
public class CeShiJiSuan {
public static void main() {
JiSuan.ShuChuJiSuanJieGuo("");
}
public static void main(String[] args) {
JiSuan.ShuChuJiSuanJieGuo("");
}
}
可以发现只有public static void main(String[] args)
才是高亮的(先不要管String[]
记住它也是诸多类型之一即可):
2.6 一个类中允许相同函数名字 - 函数重载
在2.3中的演示细心的你可能会发现,名称的定义不是不能重名吗?为什么你在同一个类中定义了两个main
函数?在相同包下不能有重复类,在相同代码快内(以及其子代码块内)不能有重复变量名,无论是用int、double、String等定义的还是通过类定义的。
试想如果你想实现播放器的随机播放:是不是就不需要你告诉它应该唱什么(不需要入参)?,与点歌播放:此时需要你告诉它需要唱什么歌。但是他们又同属于一个功能,唱歌。所以此时需要这样做:
public static void suiJiChang() {//随机唱
}
public static void dianGeChang(String geQuMingCheng) {//点歌唱
}
这在java中称之为:函数的重载,通过:
不同入参的数量
,或相同参数数量但定义的入参类型不同
来完成(从没有参数到重载100次后的有100个参数)。比如此时的dianGeChang(String geQuMingCheng)
与public static void dianGeChang(int geQuMingCheng)
,它们的区别是一个是String一个是int类型,就连定义的入参名词都一样geQuMingCheng
,也是可以的:
需要注意的是否有返回值和是否是静态方法的标识,是无法达到这种效果的,你可以试试写一下相同的入参(或有或都有一样的入参)会报错。
三、继承与多态
3.1 继承
现在我们在具体的代码中来体会继承:(场景:不同的人不同的吃饭方式)
在这个例子中,我们抽象和其他人吃饭的方式不同的场景,类图:
1.人类是我和张三继承的对象,是我们的基础,我们都拥有年龄这样的属性(所以不需要在我和张三更具体的类中反复强调)。
2.人类人类都拥有最基本的天赋,吃饭,但是有的人会用左手吃饭,大部分人会用右手。
在代码中我们需要新建三个不同的类(.java文件),人类、我、张三。并且我和张三需要继承
人类。
- 在java程序中使用
extends
关键字来实现继承
,在类名称和{}
之间,用空格分开。 - 人类(父类)中体现了人类最初是用双手来吃饭,我自己和张三则分别使用左右手拿筷子吃饭。
- 真实场景中人类可以抽象出更多的类似
nianLing
这样的特征。 //
是注释内容,在java中不会被执行。仅仅是在文件中做提示或解释用。比如大家一个团队共同完成一个项目,便于别人理解你写的代码。
package Lesson2;
//人类
public class RenLei {
int nianLing;
public void chiFan(){
System.out.println("用双手吃饭");
}
}
package Lesson2;
//我自己
public class Wo extends RenLei {
public void chiFan(){
System.out.println("用左手拿筷子吃饭");
}
}
package Lesson2;
//张三
public class ZhangSan extends RenLei {
public void chiFan(){
System.out.println("用右手拿筷子吃饭");
}
}
Tip:在java中一个类只能拥有一个父类,而父类的子类无限制。
3.2 多态
3.3 实验
刚才我们实现了三个不同的类,现在我们尝试着使用
它们:
- 这里我在和RenLei等类相同的包Lesson2下新建了Test类,用于使用
主程序入口main函数
- 现在你需要在你得工程中
完完整整
的复制它们。并且自己尝试运行或分析。
package Lesson2;
public class Test {
public static void main(String[] args) {
RenLei r = new RenLei();
Wo w = new Wo();
ZhangSan z = new ZhangSan();
r.chiFan();
w.chiFan();
z.chiFan();
}
}
需要自己注意的是如果你在IDEA中会发现我和张三类的chiFan
函数是高亮的(当然这不是最重要的,只是一个Tip),这在java中叫做重写
,这与这种写法同意(注意到@Override
了吗),这也是多态
在代码中实现的关键点:
- 和
继承的父类
完全一样的函数名称,参数,返回值
package Lesson2;
//我自己
public class Wo extends RenLei {
@Override
public void chiFan(){
System.out.println("用左手吃饭");
}
}
通过你自己运行尝试后,相信此时你并没有觉得有什么实际上的意义区别,味道差的差别尝试对比下面的代码:
package Lesson2; public class Test { public static void main(String[] args) { RenLei r = new RenLei(); RenLei w = new Wo(); RenLei z = new ZhangSan(); r.chiFan(); w.chiFan(); z.chiFan(); } }
- 在new一个新的对象的时候可以用任意对象的父类来作为
变量的定义类型
,用于接收它。但是它实际的类型是new
关键字之后的。- 这种
多态
加上函数重写
的方式,帮助我们在实际开发中并不需要知道某个类。这种情况真实的场景适用于比如:
- 当你需要让你的秘书(你的代理 你的父类)告诉某个部门经理要写工作报告了,但是他真的很忙。
- 这个时候秘书不应该给你直接反馈他没时间,而是它默认的,他来整理这个经理的工作报告,也就是说,在程序中的
默认实现
- 所以,其实子类重写父类的函数是不必要的。但如果子类有重写,通过
父类 父类变量名 = new 子类();
时,就会去调用子类重写的函数。
- 如果父类有的函数子类重写了,调用子类的
- 如果父类没有这个函数,则不存在重写
- 如果父类有子类没有,调用父类的
- 接下来自己多改动一下代码多尝试一下执行结果吧
四、构造函数
在前面的例子中我们已经使用过了
Scanner scanner = new Scanner(System.in);
中的(System.in)
。Goods g1 = new Goods();g1.proDate = "2022-2-2";
中的对象名.属性名 =
。
它们都是在为这个对象赋值
,第一种方法和函数的入参
一样。看起来就会比第二种要方便,如果有几十个属性的时候,第一种方式只需要在一句话中完成。而第二种则需要
Goods g1 = new Goods();
g1.a = "2022-2-2";
g1.b = "2022-2-2";
g1.c = "2022-2-2";
g1.d = "2022-2-2";
g1.e = "2022-2-2";
...
类的构造函数声明
要完成第一种效果,我们需要对类的定义进行修改,前面说过了它本身的意义和函数一样,只不过构造(语法)
不同罢了。代码示例:
package Lesson2;
public class Goods {
String a;
String b;
String c;
String d;
String e;
String f;
public Goods(String a, String b, String c, String d, String e, String f) {
this.a = a;
this.b = b;
this.c = c;
this.d = d;
this.e = e;
this.f = f;
}
}
- 前面说过了,
类
和函数
中定义的变量可以重名。this
就是特指在类代码块
中的变量。就是这个类
。this
不是必要的,但是如果没有this时,默认就近原则,就是如果你在函数中定义了,此时这句代码将完全无意义。因为函数的参数也属于函数定义变量
的范围,这里并没有把传进来的参数赋值给类对象中的a
,- 调用代码的示例(至于为什么是图片而不是代码。。自己尝试着多写写吧):
- IDEA工具会帮助你提示你参数的
位置顺序
,以及在当前位置的变量名字
。
类的构造函数重载
函数可以重载,构造函数也同理。每个类都会有非常多的使用场景,有些时候需要很多属性来标识,有些时候不需要:
有参无参构造函数
类的函数(方法)
和类构造函数
的参数数量的定义,以及调用时有无参数
需要相对应。也就是说在上面我们声明了多个参数的构造方法,我们就不能通过无参的方式来进行调用比如Goods g1 = new Goods();
;除非你定义了无参构造方法(函数)
五、 String?对象?实例?内存地址?
好,这节课我们尝试着来一些更深入的内容
。为什么深刻
的内容不适合时时刻刻去讲,其实就像小孩子刚出生学走路一样,这也是再次强调学习最重要的方法。学习一项新的技能的开始一定是学会模仿和使用、其次再谈深入了解。所以到这里如果你能看懂固然是好毕竟能自学也是成年人了。PS:其实也不难
细心的同学可能造就已经发现了,String和int,double的区别,首先它在语法上String更像是一个
类
。我们前面提到过,java中所有的类定义都需要首字母大写。
结合在这节课中对类的学习,java中存在着两种类型1.基础类型(int,double,float,boolean等)2.对象类型(就是类)。
String是一个什么样的类?
String是一个字符串常量
类,真正的字符基础类型
是char
。那为什么我们可以使用String a = "字符串内容";
这样的赋值方式?我们使用类时不是String a = new String("字符串内容");
吗?没错其实两种都可以原因有2:
- String是内置类,它被设计允许这样的赋值方式(特殊语法);
- 实现原理不同,我们前面提到过类与对象。
- 我们提到过类只是
模型
,你目前可以理解为它确实被java解释了,但是并没有任何实例
。 - 在你使用
new
之后才会被真正的在内存(你的内存条 非磁盘-硬盘)
中有一块属于这个对象的空间
。也就是实例
。 - 那么
String a = "字符串内容";
呢?看下面:
不同内存块?
- 使用
String a = new String("字符串内容1");
和String b = new String("字符串内容1");
和String a = "字符串内容2";
所存在的内存空间
是不同的。常量内存块
是java内部用于优化运行效率的缓存内存块
,在你运行程序时你定义的所有类的定义 也就是模型
就会被加载到该空间。常量内存块
也会对字符串进行缓存也就是当String a = "字符串内容2";
时。- 实际上String类型在使用
new
关键字初始化
时,java会在公用内存空间中
新建一个对象地址,如果常量池不存在,也会在常量池中新建一个。- 尝试思考下面的问题:【
=
号是赋值(定义变量的具体内容),==
号在java中表示比较(如果是普通类型,直接比较它们是否相等,如果是类类型(引用类型)
则比较的是内存地址)】String str = "abc"; String str1 = "abc"; String str2 = "abc"; System.out.println(str==str1);//true System.out.println(str==str2);//true String str = new String("abc"); String str1 = new String("abc"); String str2 = new String("abc"); System.out.println(str==str1);//false System.out.println(str==str2);//false System.out.println(str1==str2);//false
地址?
1.当我们使用
new
关键字来通过类模型
新建一个对象时
,java会为我们分配一个内存地址用于存储它,每一次的new
内存地址都不会相同。
2.其实就像你在磁盘上新建了不同文件一样。其实计算机就是为你分配了地址空间,只不过一个在内存条上,一个在磁盘上。(如果时间足够的话后续会讲解计算机组成原理
,目前先写怎么做比较多吧)。
对象引用?
这里为了方便你得理解,我直接在上面的图中进行修改。我们定义对象时直接将其它相同类的对象
直接赋值也是可以的。比如将上面的GoodsTest
例子修改为如下代码:
package Lesson2;
public class GoodsTest {
public static void main(String[] args) {
Goods g1 = new Goods();
g1.proDate = "2022-2-2";
Goods g2 = g1;
g2.proDate = "2033-3-3";
System.out.println(g1.proDate);
System.out.println(g2.proDate);
}
}
那么这个时候其实修改g2就是在修改g1,因为它们同时被指向了同一个内存地址,因为这里的g2
不是new
出来的。
不可变对象?
可以用String来尝试一下上面的例子。并思考,并找寻答案(算是作业了吧)。
Tips1:不同内存块,程序处理的不同内存地址不可能相同。
Tips2:new String
和String
赋值的区别。
Tips3:String
和普通类
在java中的处理完全不一样
作业:
可以研究一下如何在idea工具中进行调试
(百度其它文献)。调试能让你清楚每一句代码的执行:
- 执行顺序(为什么先走了这里)
- 为什么是这个结果(变量的改变,内存地址的改变)