JAVA第1~3次作业总结
前言
接触JAVA已经有将近一个月的时间了,感触颇多。
JAVA具有大部分编程语言所共有的一些特征,被特意用于设计用于互联网的分布式环境。根据网络上的说法,java具有类似于C++语言的形式和感觉,但相比于C++语言更易于使用,而且在编程时彻底采用了一种以对象为导向的方式。我想,等学完JAVA这门课,对我C语言一定也是有很大帮助的。
JAVA给我印象最深的就是面向对象的特点。无论是大类的设计,封装,都处处体现出面向对象的特点,因此,我们在前三周并没有过多地练习所谓的技巧与算法,而是重点在练习基本的语法并学习如何面向对象设计。每周大量的练习,对于我这种编程新手来说,属实吃力,但不得不承认我从练习中学到了大量的知识与方法,以及代码的Debug技巧等等。下将从5个方面来分析。如有不足,欢迎讨论!
目录:
二:三次作业遇到的BUG(踩坑警告)
三:代码思路与解决方法
四:作业总结与代码改进
五:一些感想
一:关于面向对象
关于面向对象,我目前学习到的是类的封装和抽象、面向对象思想,单例、关联、聚集和组合最近才学到,感触还不是特别深,等以后有了感悟会专门写。
关于类的封装和抽象,首先说封装。封装的定义就是:对象代表什么,就得封装对应的数据,并提供数据的行为。懂得封装,就会自己正确设计对象(如可以private year等),正确理解封装思想的好处,可以让编程变得更简单,有任何事找对象调用方法就可以,其次,可以降低我们的学习成本,可以少学,少记,有需要是去搜索就可以。需要谨记的是,我们的课程是OOP(面向对象的程序设计),而不是面向过程的程序设计,我们没有必要在方法上去纠结,所以封装对于面向对象,可以说是无比重要的。
关于类的抽象,在书本上的P326就提到过:类抽象是将类的实现和使用分离。事实上,类的抽象与封装是相辅相成的。类的创建者描述其功能,让使用者明白如何使用类。而从类外可以访问的公共构造方法、普通方法和数据域的集合以及对这些成员预期行为的描述,构成了类的合约(class's contract)。而类的使用者不知道类是如何实现的,实现的细节通过封装对用户隐藏起来,这就成为类的封装。这样,就很好的将类的抽象与封装结合在了一起。
关于面向对象的思想,事实上,我认为我们在学习JAVA的基础上已经有了极大的跨度,老师默认我们会循环、方法、数组等在C语言学过的知识,从而直接跳到了面向对象来讲。在默认了有一定的基础的前提下,为了实现面向对象的结果,我们就必须在有知识储备的前提下,掌握面向对象的思想。我想,从目前所做的面向对象的程序来说,我掌握了如下几个面向对象的方法:首先是JAVABEEN的正确构造方法——属性、空参、带全部参数的构造、成员方法(如set**()类get**类()),然后是正文(如设计一个大类),再对每个方法进行阐述,这是我目前的写法。事实上,面向过程范式重在设计方法,而在面向过程的程序设计中,数据和数据上的操作是分离的,而且这种做法要求传递数据给方法。面向对象程序设计数据和对它们的操作都放在一个对象中,这就可以解决很多面向程序设计的固有问题。
关于老师在课上提到的单例问题,单例模式确保某个类只有一个实例,而且自行实例化并向整个系统提供这个实例,是一种创建对象的绝佳方式。在计算机系统中,线程池、缓存、日志对象、对话框、打印机、显卡的驱动程序对象常被设计成单例。每台计算机可以有若干通信端口,系统应当集中管理这些通信端口,以避免一个通信端口同时被两个请求同时调用。总之,选择单例模式就是为了避免不一致状态,避免政出多头。这是我们在面向对象时所需要额外注意的。
二:三次作业遇到的BUG(踩坑警告)
1:OOP训练集01
OOP训练集01难度并不大,主要考验我们JAVA的知识储备以及对JAVA语言的初级理解,笔者认为主要的问题存在于7-5、7-8、7-12。
7-5BUG:在用StringBuilder时为用str.append(input.next())导致程序出错,对stringbuilder理解不充分。
7-8BUG:未注意到开头的空格
7-12BUG:多加了一个System.out.print(n)
2:OOP训练集02
OOP训练集02难度延续了OOP训练集一的难度,整体较易,主要的问题在于7-8和7-9。
7-8BUG:判断三角形时当为三角形时会直接输出General triangle但不会输出Right-angled triangle、Isosceles triangle、Equilateral triangle等,每个判断完成后没有及时进行返回。
7-9BUG:未设置数组存放每个月的日期、为判断月份为12月的情况。
3:OOP训练集03
OOP训练集03可以说是目前做过的最难的一次,尤其是7-4做了非常多的时间(一个晚上+一个下午)。主要的BUG就在7-4上。
7-4BUG:下n天:整型数最大值测试答案错误,并且在下n天纠结了很久。
三:代码思路与解决方法
这里主要对于前三次作业我认为最典型的四个问题进行代码思路的分析并展示问题的解决方法
OOP01 7-8 从一个字符串中移除包含在另一个字符串中的字符

代码实现:
import java.util.Scanner; public class Main{ public static void main(String args[]){ Scanner sc=new Scanner(System.in); String s1=sc.next(); String s2=sc.next(); String s3=""; for(int i=0;i<s1.length();i++) { int flag=1; for(int j=0;j<s2.length();j++){ if(s1.charAt(i)==s2.charAt(j)) { flag = 0; break; } } if(flag==1) s3+=s1.charAt(i); } System.out.print(s3); } }
代码思路:首先String两个新的字符串,并把空格单独提出来。空格是大家容易忽略的一个问题,在这里很难考虑到,我也是经过数次调试后才发现的。创建对象和字符串后,对S1和S2分别进行一个循环,同时int一个flag进行判断:如果S1和S2中有相同的部分,flag就视为0(即为假),此时循环结束,如果flag为1(即为真)S3就等于S1.charAt(i)+S3,最后输出S3。我想这里最巧妙的做法就是单独把空格提出来,这样就避免了后续循环中要对空格进行讨论的麻烦情况。这种把特殊单独提取出来进行讨论,然后再对常规进行分析的方法知道我们借鉴。
OOP01 7-12:列出最简真分数序列,按递增顺序依次列出所有分母为N(10 <= N <= 40),分子小于N的最简分数。

代码实现:
import java.util.Scanner; public class Main{ public static void main(String[] args){ Scanner sc=new Scanner(System.in); int n=sc.nextInt(); //System.out.print(n); for(int i=1;i<n;i++) {int flat=0; for(int j=2;j<n;j++) { if(i%j==0&&n%j==0) flat=1; } if(flat==0) { System.out.printf("%d/%d,",i,n); }flat=0; } } }
代码分析:这一题有一点算法的思维在里面。我们要按递增顺序依次列出所有分母为N(10 <= N <= 40),分子小于N的最简分数,首先先要对分子采取穷举法,利用最大公约数的办法,判断分子与40是否构成真分数。
而后从1--39判断是否跟40互质,也就是判断1--39中跟40的最大公约数为1的数。我一开始错误的思路是用40/i看有没有余数,但这种方法是错误的。再对所做的题目有了一定的思路后,我们再来动手写代码。创建对象后int一个n与i,i为分子,n为分母。首先对i进行循环,也就是对应的对分子采取穷举法,当i%j==0且n%j==0时,这说明i与n是互质的,没有一个数能再让两个数组成的分数在化简。所以直接输出i/n即可。所以,在拿到题的时候,不用直接下手写代码,而是应该分析思路,找到解决问题的入口,再进行代码实现。
OOP02 7-8判断三角形
判断三角形类型,输入三角形三条边,判断该三角形为什么类型的三角形。

代码实现:
import java.util.Scanner; public class Main { public static int wrong(double a, double b, double c) { if (a < 1 || a > 200 || b < 1 || b > 200 || c < 1 || c > 200 ) { System.out.print("Wrong Format"); return 1; }else if( a+b<=c || b+c<=a || c+a <=b){ System.out.print("Not a triangle"); return 1; }else return 0; } public static int dengyaozhijiao(double a, double b, double c) { if ((a == b || a == c || b == c) && (a * a + b * b - c * c < 0.1|| a * a + c * c - b * b < 0.1 || c * c + b * b - a * a<0.1)) { System.out.print("Isosceles right-angled triangle"); return 1; } else return 0; } public static int dengbian(double a, double b, double c) { if (a == b && a == c && b == c) { System.out.print("Equilateral triangle"); return 1; } else return 0; } public static int dengyao(double a, double b, double c) { if (a == b || a == c || b == c) { System.out.print("Isosceles triangle"); return 1; } else return 0; } public static int zhijiao(double a, double b, double c) { if (a * a + b * b == c * c || a * a + c * c == b * b || c * c + b * b == a * a) { System.out.print("Right-angled triangle"); return 1; } else return 0; } public static int sanjiao(double a, double b, double c) { if (a + b > c && a + c > b && b + c > a) { System.out.print("General triangle"); return 1; } else System.out.print("Not a triangle"); return 0; } public static void main(String[] args) { double a = 0; double b = 0; double c = 0; int flag = 0; Scanner scanner = new Scanner(System.in); a = scanner.nextDouble(); b = scanner.nextDouble(); c = scanner.nextDouble(); flag = wrong(a, b, c); if (flag == 1) return; flag = dengyaozhijiao(a, b, c); if (flag == 1) return; flag = dengbian(a, b, c); if (flag == 1) return; flag = dengyao(a, b, c); if (flag == 1) return; flag = zhijiao(a, b, c); if (flag == 1) return; flag = sanjiao(a, b, c); if (flag == 1) return; } }
代码分析:乍一看,题目的大意是无比简单的,就是让你去判断三角形的形状,但是随之问题也就来了,比如如果让你确定三角形为General triangle时,假入三角形为Isosceles triangle,你怎样让三角形输出Isosceles triangle而非General triangle?对于直角三角形的判断,真的用a*a+b*b=c*c就可以了吗?事实上当你这样写过后,发现是有误差的,因为a,b,c不一定是整数,假如说a取5.666,b取6.777,那么就算c取精确的a*a+b*b,因为精确度的问题,所以也是有极小误差的,而这种方法在这种情况下就不可取。在这里发现了PTA测试点中的一个BUG:当为等腰直角三角形的时候,直接用a*a+b*b=c*c就不行,但是在直角三角形这个测试点时,用a*a+b*b=c*c就可以。但很显然最好还是用如上代码等腰直角三角形中的方法。
对题目有了简单地分析后,我们可以开始写代码了。事实上我的方法很蠢(只是过了),后文会介绍更好的方法,但在此文中就展示自己最初的方法。首先对错误的数据进行判断,这里需要注意的是每次有错误或者正确的值都需要返回,然后再对等腰直角、等边、等腰、直角和正常三角逐一进行判断,在这里我的判断方法是在每一个函数里都int一个flag。在对直角这个条件做判断时,我们应该用a*a+b*b-c*c<0.1的情况,而非前文提到过的直接判断的方法。而对于三角形的错误输入,我的解决方法是在开头和结尾各进行一个输出错误的判断,这样就能避免例如输入2,2,3而输出“等腰三角形”的局面。这道题的思维并不复杂,但是考验我们对于返回值和细节的重视程度。
OOP03 7-3 日期类设计
定义一个类Date,包含三个私有属性年(year)、月(month)、日(day),均为整型数,其中:年份的合法取值范围为[1900,2000] ,月份合法取值范围为[1,12] ,日期合法取值范围为[1,31] 。

代码实现:
import java.util.*; public class Main{ public static void main(String []args){ Scanner sc = new Scanner(System.in); Date date = new Date(); date.Date(sc.nextInt(),sc.nextInt(),sc.nextInt()); if(date.checkInputValidity()) date.getNextDate(); } } class Date{ private int year; private int month; private int day; int [] mon_maxnum={0,31,28,31,30,31,30,31,31,30,31,30,31}; public void Date(int year,int month,int day){ this.year=year; this.day=day; this.month=month; } public int getYear() { return year; }public void setYear(int year) { this.year = year; }public int getMonth() { return month; }public void setMonth(int month) { this.month = month; }public int getDay() { return day; }public void setDay(int day) { this.day = day; }public boolean isLeapYear(int year){ if((year%400==0)||(year%100!=0&&year%4==0)){ mon_maxnum[2]++; return true; } return false; } public boolean checkInputValidity(){ if(this.isLeapYear(year)==true) { mon_maxnum[2]=29; } else { mon_maxnum[2]=28; } if(this.year<1900||this.year>2000||this.month<1||this.month>12||mon_maxnum[this.month]<this.day||this.day<1) { System.out.println("Date Format is Wrong"); return false; } else { return true; } } public void getNextDate(){ if(month==12&&day==31) System.out.printf("Next day is:%d-%d-%d",++year,1,1); else{ if(day==mon_maxnum[month]){ System.out.printf("Next day is:%d-%d-%d",year,++month,1); } else{ System.out.printf("Next day is:%d-%d-%d",year,month,++day); } } } }
代码分析:类图如下:



代码的深度、最大深度、和最大复杂性都处于绿框内,但是能看到方法这一栏处于白色阶段,所以需要我们再进行优化和改进。语句和深度均处在较为简单的第二阶段。所以说明这题的难度也并不大。
这是一个很典型的面向对象的问题。我们需要定义主函数,设计大类,写成标准的JAVABEEN形式,然后再逐一对每个模块的功能进行实现。题目的思路很简单,这里着重对具体内容进行分析。闰年的判断我们已经做过很多次,不再赘述;对于年份的正确输出,则是在基于闰年的判断基础上再进行;对于获取下一天,思路则是:如果月份为12且天数为31,则年数加1,月份和天数都变为1,如果天数等于前面数组里设的最大天数,则月份加1,天数变为1,两者都不是的话天数加1即可,这种由特殊一步步到普通的思路值得我们学习。
OOP03 7-4 日期类设计

代码实现:
import java.util.Scanner; public class Main { public static void main(String[] args) { Scanner input = new Scanner(System.in); int year = 0; int month = 0; int day = 0;
int choice = input.nextInt(); if (choice == 1) { // test getNextNDays method int m = 0; year = Integer.parseInt(input.next()); month = Integer.parseInt(input.next()); day = Integer.parseInt(input.next()); DateUtil date = new DateUtil(year, month, day); if (!date.checkInputValidity()) { System.out.println("Wrong Format"); System.exit(0); } m = input.nextInt(); if (m < 0) { System.out.println("Wrong Format"); System.exit(0); } System.out.print(date.getYear() + "-" + date.getMonth() + "-" + date.getDay() + " next " + m + " days is:"); System.out.println(date.getNextNDays(m).showDate()); } else if (choice == 2) { // test getPreviousNDays method int n = 0; year = Integer.parseInt(input.next()); month = Integer.parseInt(input.next()); day = Integer.parseInt(input.next()); DateUtil date = new DateUtil(year, month, day); if (!date.checkInputValidity()) { System.out.println("Wrong Format"); System.exit(0); } n = input.nextInt(); if (n < 0) { System.out.println("Wrong Format"); System.exit(0); } System.out.print( date.getYear() + "-" + date.getMonth() + "-" + date.getDay() + " previous " + n + " days is:"); System.out.println(date.getPreviousNDays(n).showDate()); } else if (choice == 3) { //test getDaysofDates method year = Integer.parseInt(input.next()); month = Integer.parseInt(input.next()); day = Integer.parseInt(input.next()); int anotherYear = Integer.parseInt(input.next()); int anotherMonth = Integer.parseInt(input.next()); int anotherDay = Integer.parseInt(input.next()); DateUtil fromDate = new DateUtil(year, month, day); DateUtil toDate = new DateUtil(anotherYear, anotherMonth, anotherDay); if (fromDate.checkInputValidity() && toDate.checkInputValidity()) { System.out.println("The days between " + fromDate.showDate() + " and " + toDate.showDate() + " are:" + fromDate.getDaysofDates(toDate)); } else { System.out.println("Wrong Format"); System.exit(0); } } else{ System.out.println("Wrong Format"); System.exit(0); } } } class DateUtil{ private int year,month,day; private int yearDay=365; int [] mon_maxday=new int[]{0,31,28,31,30,31,30,31,31,30,31,30,31}; public DateUtil(){ } public DateUtil(int year,int month ,int day){ this.year=year; this.day=day; this.month=month; } public int getYear() { return year; } public int getMonth() { return month; } public int getDay() { return day; } public void setYear(int year) { this.year = year; } public void setMonth(int month) { this.month = month; } public void setDay(int day) { this.day = day; } public boolean checkInputValidity(){//检测输入的年、月、日是否合法 if(isLeapYear(year)) mon_maxday[2]=29; if(year<1820||year>2020||month>12||month<1||day<1||day>mon_maxday[month]){ return false; } return true; } public boolean isLeapYear(int year){//判断year是否为闰年 if((year%100!=0&&year%4==0)||(year%400==0)){ mon_maxday[2]=29; yearDay++; return true; } return false; } public DateUtil getNextNDays(int n){//取得year-month-day的下n天日期 while (n > 365) { if (this.isLeapYear(year) == true && month <= 2) {//判断这一年的下一年是否为闰年 if (month == 2 && day == 29) { month = 3; day = 1; } year++; n =n-366; } else if(this.isLeapYear(year+1) && month > 2) { year++; n=n-366; } else { year++; n=n-365; } } for (int i = 0; i < n; i++) { day++; if(this.isLeapYear(year) == true && month == 2) {//为闰年且月份为2 if(day > 29) { month++; day = 1; } } else if (day > mon_maxday[month]) { month++; day = 1; if (month > 12) { year++; month = 1; } } } return this; } public DateUtil getPreviousNDays(int n){ DateUtil d = new DateUtil(this.year,this.month,this.day); for (int i = 0; i < n; i++) { if(isLeapYear(d.getYear()))//判断是否为闰年 mon_maxday[2] = 29; else mon_maxday[2] = 28; d.day--; if(d.day == 0){//日期减为0 d.month--; if(d.month == 0){//月份减为0 d.year--; d.month = 12; } d.day = mon_maxday[d.month]; } } return d; } public boolean compareDates(DateUtil date){ if(this.year > date.getYear()) return true; else if(this.year == date.year && this.month > date.month) return true; else { if(this.month>date.month) return true; else if(this.month<date.month) return false; else{ if(this.day>date.day) return true; else if(this.day<date.day) return false; } } return true; } public boolean equalTwoDates(DateUtil date){ return this.year == date.year && this.month == date.month && this.day == date.day; } public int getDaysofDates(DateUtil date){ int res = 0; if(this.equalTwoDates(date)) return 0; if(this.compareDates(date)){ while(true){ if(isLeapYear(date.year)) mon_maxday[2] = 29; else mon_maxday[2] = 28; date.day++; if(date.day == mon_maxday[date.month]+1){ date.month++; date.day = 1; } if(date.month == 13){ date.year++; date.month=1; } if(date.year == year && date.month == month && date.day == day){ break; } res++; } }else{ while(true){ if(isLeapYear(year)) mon_maxday[2] = 29; else mon_maxday[2] = 28; day++; if(day == mon_maxday[month] + 1){ month++; day = 1; } if(month == 13){ year++; month = 1; } if(date.year == year && date.month == month && date.day == day){ break; } res++; } } return res + 1; } public String showDate(){ return this.year + "-" + this.month + "-" + this.day; } }
代码分析:类图如下:

代码的系统分析如下:


能明显看到,这题的最大复杂性已经超过了系统所认为能接受的范围内,这说明方法过于烦杂,有很多值得改进的地方。同时,代码与深度的比值程度也集中在3阶段。可以看出这道题的难度相比上一题有大幅度的提升,那么如何形成思路,运用思路进行代码实现,就是我们所要着重考虑的事。与上题的思路一样,定义主函数,写成标准的JAVABEEN形式后,再对每个模块逐一分析。但是每个模块的难度和代码量有明显的上升,故对部分进行详细分析。
首先,检测输入年月日是否合法和判断是否为闰年较为容易,不再分析。
取得下n天:根据由特殊到复杂的情况,可先分为n大于或小于365的大情况,然后在每个情况下再逐一分析,如图所示。

有了清晰的思路后,进行代码实现就不困难了。需要注意的是当不是闰年的时候,n是减去365而不是366;同时,对于n<365的情况,只需要判断月份为2和不为2的情况,而不是像n>365时需要判断n<=2和n>2的情况,因为只是在一年间进行判断。
取得前n天:事实上判断下n天的方法是复杂的,你可以分别定义平年和闰年的数组进行判断(但我当时没有想到),我试图取得下n天和前n天用同一种方法,但是两者的差别是巨大的,因为特殊情况下前n天的天数并不是如下n天一样只为1,而有可能是28,29,30,31这四种情况,所以我采用了另一种较为简单的方法,不去考虑这些,先判断mon_maxday在是否为闰年下的不同情况,再让天数递减,当递减为0时,月份递减,月份递减为0时,年递减,此时月份为12,天数再递减。重复循环这样的情况,而不是像前文一样详细的分类把各种情况写出来。
比较当前日期与date的大小:这部分较为简单,先从年开始比较,两者年份都不同时,不需要看月份和天数就可以直接比较出结果;当年相等时,对月份进行比较;当月份相等时,再对天数进行比较,最后return true即可。。
判断两个日期是否相等:直接用this.*与date.*比较即可。
求当前日期与date之间相差的天数:最后一个应该说是难度最大的。鄙人不才,但是记得高中数学老师说过:让你求什么,当你实在找不到间接量的时候,你就设什么,再去找量与量之间的关系。这题就是一个很好的例子,看起来求两个日期之间相差的天数较为复杂,但你可以直接设这个答案即(res),在满足条件的情况下,res++即可。接下来确定条件即可,根据前面的经验,从特殊到正常,肯定先从闰年入手,对mon_maxday进行判断,再让date.day自加,当这一天等于mon_maxday[]中的天数加1,也就是超过月份中的最大天数时,date.month++;当date.month为13时,年份自加,当加到年份月份天数都相同时,说明相差的日期已经被加完了,break这个循环,让res++就行。我们不能确定当前日期和date之间谁大谁小,所以我们让两者身份互换,再进行一次相同的过程即可。最后要记得日期之间的差值是两者的差值再加1,所以要return res+1。
四:作业总结与代码改进
没有人可以说自己第一遍写得代码就是完美的,事实上我的代码很多地方都有极大的进步空间。目前我的状态:诋毁答辩,质疑答辩,理解答辩,成为答辩(bushi)。当然我希望能在造出答辩的基础上,努力做到以后少造答辩,多写能让人看的下去的代码,下举几例说明如何优化自己的代码。
OOP01 7-8:
public class Main{ public static void main(String args[]){ Scanner sc=new Scanner(System.in); String s1=sc.nextLine(); String s2=sc.nextLine(); s1=s1.replaceAll("["+s2+"]",""); System.out.print(s1); } }
一个replaceAll函数就可以轻松解决这个问题。
OOP02 7-8
import java.util.*; public class Main{ public static void main(String args[]){ Scanner sc=new Scanner(System.in); float []side=new float[3]; for(int i=0;i<3;i++) side[i]=sc.nextFloat(); if(side[0]<1||side[0]>200||side[2]<1||side[2]>200||side[1]<1||side[1]>200){ System.out.print("Wrong Format"); return; } if(side[0]+side[1]<=side[2]||side[0]+side[2]<=side[1]||side[1]+side[2]<=side[0]){ System.out.print("Not a triangle"); return; } int i,j; for(i=0;i<3;i++){ for(j=i+1;j<3;j++) if(side[i]==side[j]&&side[j]==side[3-i-j]){ System.out.print("Equilateral triangle"); return; } } for(i=0;i<3;i++){ for(j=i+1;j<3;j++) if(side[i]==side[j]&&Math.abs(side[i]*side[i]+side[j]*side[j]-side[3-i-j]*side[3-i-j])<10e-6){ System.out.print("Isosceles right-angled triangle"); return; } } for(i=0;i<3;i++){ for(j=i+1;j<3;j++) if(side[i]==side[j]){ System.out.print("Isosceles triangle"); return; } } for(i=0;i<3;i++){ for(j=i+1;j<3;j++) if(side[i]*side[i]+side[j]*side[j]==side[3-i-j]*side[3-i-j]){ System.out.print("Right-angled triangle"); return; } } System.out.print("General triangle"); } }
无需用方法一个个比较边之间关系,利用数组和循环可以巧妙解决问题。
总而言之,代码的优化是永无止境的,我还有很长的路要走。
五:一些感想
关于一些问题的总结
对于作业的总结,我格外珍惜,这是提升自我,查漏补缺的最佳时机。这里很想对几个一直很困惑的问题进行分析,这里试举一例。
有一个很经典的面试题:String str1 = "abc"; // 在常量池中 String str2 = new String("abc"); // 在堆上
这两者到底有何区别?
当直接赋值时,字符串“abc”会被存储在常量池中,只有1份,此时的赋值操作等于是创建0个或1个对象。如果常量池中已经存在了“abc”,那么不会再创建对象,直接将引用赋值给str1;如果常量池中没有“abc”,那么创建一个对象,并将引用赋值给str1。那么,通过new String(“abc”);的形式又是如何呢?答案是1个或2个。当JVM遇到上述代码时,会先检索常量池中是否存在“abc”,如果不存在“abc”这个字符串,则会先在常量池中创建这个一个字符串。然后再执行new操作,会在堆内存中创建一个存储“abc”的String对象,对象的引用赋值给str2。此过程创建了2个对象。当然,如果检索常量池时发现已经存在了对应的字符串,那么只会在堆内创建一个新的String对象,此过程只创建了1个对象。(摘自CSDN)对此,我专门对string进行了总结:
1:被final修饰的类,不能被继承
2:支持序列化和可以比较大小
3:String底层是通过char类型的数据实现的,并且被final修饰,所以字符串的值创建之后就不可以被修改,具有不可变性。
总结1:String字符串具有不可变性,当字符串重新赋值时,不会在原来的内存地址进行修改,而是重新分配新的内存地址进行赋值
总结2:当字符串进行拼接时,也不会在原来的内存地址进行修改,而是重新分配新的内存地址进行赋值。
总结3:当调用String的replace方法修改指定的字符或字符串时,也不会在原来的内存地址进行修改,也是重新分配新的内存地址进行赋值
我想,一个小小的string都有这么多值得我去研究的,可以说未来的道路任重而道远啊。
SRP
关于面向对象,有一个很经典的职责,叫做SRP,俗称单一职责,这也是OOP中非常关键的一环。单一职责原则的英文是 Single Responsibility Principle,缩写为 SRP。翻译过来就是:一个类或者模块只负责完成一个职责(或者功能)。所谓职责是指类变化的原因。如果一个类有多于一个的动机被改变,那么这个类就具有多于一个的职责。而单一职责原则就是指一个类或者模块应该有且只有一个改变的原因。相信在我们日常开发中,每个人都遇到过改了一个问题经常会引起另一个问题。其原因大多都是因为在设计上违背了单一职责原则。如果一个类承担的职责过多,就等于把这些职责耦合在一起了。一个职责的变化可能会削弱或者抑制这个类完成其他职责的能力。这种耦合会导致脆弱的设计,当发生变化时,设计会遭受到意想不到的破坏。而如果想要避免这种现象的发生,就要尽可能的遵守单一职责原则。此原则的核心就是解耦和增强内聚性,是为了实现代码高内聚、低耦合,提高代码的复用性、可读性、可维护性。(摘自CSDN)所以,一个函数,一段代码,不是看你写得有多好,有多长,而是你能不能就通过这个代码实现对应的职责而不做任何影响运行效率的事情。
对于未来的看法
最近chatgpt大火,各类功能层出不穷,写出代码的可读性和实用性极高,引得人们直呼:程序员未来会被替代吗?搞软件,这个好像被视为“天之骄子”的职位,一下就蒙上了一层阴影。我会被替代吗?我不知道,我以后又必须要做程序员吗?我也不知道。记得高考前好像对自己的人生有着无比清晰的规划,但是在分数出来后,我走上了一条自己未曾设想的道路。写博客,编程序,画仪表,这些我以前从来不敢想的事情,我现在竟也能做一点了。每一段代码,每一个知识点,对我而言都是无比崭新的。我时常想:明天的自己和今天的自己会有多大的差别呢?每一天都是充满挑战的,有太多不会的、不懂的等着我去发掘。所以我并不焦虑,就着眼于现状吧,至于未来何去何从,就交给时间和命运吧。我做好我该做的,剩下的,即便我想改变,也是无能为力的。你问我对编程有多喜欢?我能说我很讨厌吗......但我想不会有很多事能让你做的舒舒服服的,学业如此,工作也是如此,有几个人能真正热爱自己所做的事呢?
这个博客是老师布置的任务,我会定期的更新,但如果以后有时间,我是说如果有时间,我也很想发表一些萌新对于编程的一些看法。今天就到这里吧,我又要去造答辩了(bushi)。
浙公网安备 33010602011771号