《Java程序设计》第四章-认识对象
20145221《Java程序设计》第四章-认识对象 总结
教材学习内容总结
类与对象
- 定义:对象是Java语言中重要的组成部分,之前学过的C语言是面向过程的,而Java主要是面向对象的。Java中变量有2种类型,一个是基本类型,另一个则是类类型。使用Java撰写程序几乎都是在使用对象,要产生对象必须先定义类,类是对象的设计图,对象是类的实例。
- 特点:
- 有别于C语言的程序编写,在用Java编写中,如果需要什么功能,我们就可去找一个对象,而这个对象就包含这个功能,然后通过new建立对象,通过“.”来调用该类的一些功能。
- 其中要能理解对象的含义,例如
Clothes c1 = new Clothes();
,其中c1是在栈内存中产生,而对象c1则是产生在堆内存中,c1可以近似看成指向堆内存中的指针。 - 书上提供了一个很好的方法正确理解其中的本质,那就是画图,形象又直观,所以在涉及对对象的理解时,一是可以画图,二是可以把相关代码贴在电脑上运行一下。还有一些常用的标准类Scanner,BigDecimal等都大大方便了我们编程过程。
- 注意:对象相等性,首先要明白对于类类型的变量来说==和基本类型中的==有本质上的区别,因为是类类型,所以==表示的是这个类产生的2个对象是否是同一个对象,如果是同一个对象,那么==的返回值才是true,否则为false;如果想要比较2个对象的内含值,应该要用
a.equals(b);
。其实理解其最好的方法就是画图,例如课本P89页中的代码,以及后面相关知识的介绍,都是通过画图来理解的。
基本类型打包器
- 概述:在上一章已经学习了基本类型的变量,但是对于Java程序语言来说,基本类型的变量效率往往不高效,Java的特点在于面向对象,所以我们也可以把这些基本类型的变量打包成对象之中,这样我们就可以像操作对象那样操作这些原本是“基本类型”的变量了。
- 特点:
- 开始在学习这一节内容的时候,不能理解,明明是基本类型了,为什么还要把大费周章地又打包成类类型。在最后编写本章的操作题时,我就明白了,编写Java时一定要把固有的“面向过程”的思想转化为“面向对象”,对象可以提供我们许多功能,简化我们的编程,这在后续学习中会更加明白。
- 除了打包,J2SE 5.0之后,还能自动装箱、自动拆箱,在我的理解看来,就是不需要严格的像一般建立对象那样,可以简便一些(自动装箱与拆箱的功能事实是编译程序蜜糖),例如:
Integer wrapper = 10; // 自动装箱
int foo = wrapper; // 自动拆箱
- 注意:既然打包为了对象,当然也要满足对象的特点,尤其是判断“相等”。作为编程者,一定要弄明白我们的目的。在建立对象之后,如果是想比较这2个对象的内含值,则一定一定要使用
a.equals(b);
这种形式的比较方式,只要把握了这一点,就可以避免课本P97这样的错误。
数组对象
- 概述:数组在Java中就是对象,牢牢把握这个概念。
- 特点:对象的一些特点性质都可以在数组中使用。定义数组的方法,如果知道是哪些具体的数,则可以如课本P95一样,不知道具体的数可以像P98一样。在定义二维数组时,也可以仿照一维数组进行定义,总之,数组就是对象,这是数组最大的特点。
- 注意:
- 既然数组是对象,那么对象需要注意的性质,数组都要注意。对于数组本身来说,不能超过其索引范围,不然会报错:ArrayIndexOutOfBoundsException(编译时不会报,运行时会报错)。
- 注意各种类型的数组初值情况(课本P98)。
- 再就是要理解二维数组的本质,二维数组,其实是在数组的基础上对于每个元素,再建立一个数组(对象),只是在很多编程实例中体现出了“二维”、“矩阵”等形式,本质还是在数组的每个元素上再建立数组。认识到这一点,加上课本的图4.5、4.6、4.7、4.8理解起来就容易多了。
- 数组的复制,首先由2个方法可以用System.arraycopy()与Arrays.copyof(),可以通过这些类快速复制一个数组,不过在调用时要注意括号中参变量的含义,类型内容都要一致。在使用了上述方法后,要明白一点我们进行的都是“浅层复制”,是没有连同对象一起复制的。如果想要深层复制,则需自己操作,敲代码完成自行复制元素,如课本P106。
字符串对象
- 概述:字符串本质是打包字符数组的对象,是java.lang.String类的实例。
- 特点:既然是对象,当然也会有很多功能,length(),charAt(),toUpperCase()等。通过一些方法Byte.parseByte(number)等还可以将字符串剖析为基本类型。
- 注意:
- 字符串池:如果直接将一串字符指定给2个字符串变量,则这2个字符串变量会参考到同一对象。因为在Java中,为了效率考虑,只要""包括的字符串内容相同,无论在程序代码中出现多少次,JVM都只会建立一个String实例,并在字符串池中维护。
- 不可变动字符串:必须知道的是:在Java中,字符串对象一旦建立,就无法更改对象中的任何内容,对象上没有任何一个方法可以更改字符串内容。使用+字符可以达到这样的效果,不过根据反编译的过程,可以发现,实际上是产生了新的字符串对象。而大量的产生新对象又是不希望看到的,所以我们可以用StringBuilder来改善,它的特点在于每次调用完后都会返回原有的StringBuilder对象,这样可以避免产生多个对象。
查询Java API文件
- 概述:从本章的实际操作题和课本上的范例程序中,都会发现每个程序几乎都使用了不同的类,如java.util.Scanner、java.math.BigDecimal、基本类型打包器等等。在以后的编程中,如果我们想用某一个类的某一功能,但不知道如何调用,或者想了解一下某一个类具体有哪种功能等内容,就必须要通过查询Java API文件了。
- 方法:
- 通过Java官方网站;
- 直接通过搜索引擎搜索相应的类,就会显示对应的文件说明了;
- 以上2种方法都是在线查询Java API文件,还可以离线查询。在Windows下可以下载CHM格式的Java API。如图,这样会更加的方便。
教材学习中的问题和解决过程
-
其实这一章内容开始不是很好理解,因为之前对对象没有一个概念。所以学的有点慢,但是按照老师说的方法,一个是认真看课本,另一个就是学编程必须要养成的习惯,勤敲代码。可能开始对书上给的一些代码,还不能理解,面对书上画的图,也不能透彻明白。但是好记性不如烂笔头,编程也一样,只要把这些代码敲一遍,编译运行一编,看看结果,这样印象可能会深一点,对于代码的领悟可能更也更好一些。
-
在最开始接触对象时,有一种感觉就是感觉“类”有点似曾相识,感觉和C语言中的结构体很像。不过我知道C语言是面向过程的,Java是面向对象的,所以我认为结构体和类还是应该有本质区别的。翻开原来的C语言书,发现结构体好像只是把不同类型的变量打包在了一个“新的类型变量中”,并不能在结构中定义功能,而类中还可以构造函数。类的功能应该更强大更灵活。
-
按照老师的指导,对书中P112的效率进行了测试:
(1). 代码(只展示第一种,后两种只是将“测试代码段”用课本上的填充进去):public class TestJavaClass{ public static void main(String[] args){ //获取开始时间 long startTime = System.currentTimeMillis(); //测试代码段 for(int i=1; i<101; i++){ System.out.print(i); if(i != 100){ System.out.print('+'); } } System.out.println(); //获取结束时间 long endTime = System.currentTimeMillis(); System.out.println("程序运行时间: "+(endTime-startTime)+"ms"); } }
(2). 运行结果:
第一种:
第二种:
第三种:
(3). 结论:通过上述操作,确实可以发现第三种的效率最高。
代码调试中的问题和解决过程
课后操作题
Fibonacci数列:
-
代码:
import java.util.Scanner; public class Fibonacci{ public static void main(String[] args){ System.out.printf("求几个费氏数?"); Scanner scanner = new Scanner(System.in); int n = scanner.nextInt(); if(n == 1) System.out.print(0); else{ int[] fiboNums = new int[n]; fiboNums[1] = 1; System.out.print(0 + " " + 1); for(int i=2; i<n; i++){ fiboNums[i] = fiboNums[i-2] + fiboNums[i-1]; System.out.print(" " + fiboNums[i]); } } System.out.println(); /* fiboNums[1] = 1; for(int i=2; i<n; i++){ fiboNums[i] = fiboNums[i-2] + fiboNums[i-1]; } for(int fiboNum : fiboNums){ System.out.print(fiboNum + " "); } System.out.println(); */ } }
-
运行结果:
-
结论:第一次编译该程序的时候出现了乱码,可能跟我这个代码要打印汉字有关吧。经过查找了相关资料,只要在编译时输入
-encoding utf-8
即可解决。斐波那契数列不算难,不断迭代就可解决。程序中被注释的代码是最开始编的,最后想到也可以用一个for循环解决,不过感觉2个for循环看起来结构更清楚一些,可读性更强一些。
洗牌:
-
代码:
public class Shuffle{ public static void main(String[] args){ String[] pokers = { "梅1","梅2","梅3","梅4","梅5","梅6","梅7","梅8","梅9","梅10","梅J","梅Q","梅K", "砖1","砖2","砖3","砖4","砖5","砖6","砖7","砖8","砖9","砖10","砖J","砖Q","砖K", "桃1","桃2","桃3","桃4","桃5","桃6","桃7","桃8","桃9","桃10","桃J","桃Q","桃K", "心1","心2","心3","心4","心5","心6","心7","心8","心9","心10","心J","心Q","心K" }; //定义一个0-51的随机数(不重复)数组。 int[] numbers = new int[pokers.length]; //赋初值,简化判断是否有重复随机数的循环次数。 for(int i=0; i<pokers.length; i++){ numbers[i] = -1; } int num; boolean flag; for(int i=0; i<pokers.length; i++){ //产生随机数。 while(true){ flag = true; num = (int) (Math.random() * pokers.length); //凡是搜索到了-1就表示已经搜索完毕。 for(int j=0; numbers[j]!=-1 ;j++){ if(numbers[j] == num){ flag = false; break; } } //不重复,就向随机数数组中赋值。 if(flag){ numbers[i] = num; break; } } } /* 调代码时用到,用以判断是否成功生成了0-51的不重复随机数。 for(int number : numbers){ System.out.print(number + " "); } System.out.println(); */ for(int i=0; i<pokers.length; i++){ System.out.printf("%-4s",pokers[numbers[i]]); if((i+1)%13 == 0) System.out.println(); } } }
-
运行结果:
-
结论:拿到这个题目,想了一会,考虑怎样才能让52张扑克牌随机输出。最后想到了可以用随机数产生的方法,0-51个随机数对应的其实就是“扑克牌数组”的角标,因为角标的随机,实现了出牌的随机。当然因为编写不熟悉,输出的扑克牌有重复的,为了能更加看清楚随机数(不重复)是否成功产生,打印了52个随机数(代码中被注释的片段)。最后发现过然是随机数产生有问题,逆推回去,发现了第二个for循环中判断的条件原来写的是“numbers[j]!=0”,当时的想法是如果碰到0了,说明已经搜索完了(int型数组默认赋初值为0),不用往后搜了,但是忽略了一点,产生的随机数也含有0,如果采取这样的方法判断,就会出现重复的情况。所以在前面加了一句,将数组中的元素赋初值为1。最后打印时发现,“梅10”“心10”等这4张牌多占一个字符的宽度,所以为了打印美观,将输出格式控制为%-4s。
排序:
-
代码:
import java.util.Arrays; public class BubbleSort{ public static void main(String[] args){ int[] number = {70, 80, 31, 37, 10, 1, 48, 60, 33, 80}; /* 冒泡排序代码: int temp; for(int j=0; j < number.length-1; j++){ for(int i=0; i < number.length-1-j; i++){ if(number[i] > number[i+1]){ temp = number[i]; number[i] = number[i+1]; number[i+1] = temp; } } } */ Arrays.sort(number); for(int num : number){ System.out.printf("%3d",num); } System.out.println(); } }
-
运行结果:
-
结论:排序题其实在C语言中也接触了不少,代码中被注释的片段用到的就是冒泡排序,可以说比较简单高效。但因为Java面向对象的特性,可以用
Arrays.sort(number);
,一句代码,直接将原来的数组从小到大排列,更加简单。既然再学Java,就要多使用对象,这样可以提高编程技能和效率。
查询:
-
代码:
(1)产品代码:import java.util.Scanner; public class Search{ public static void main(String[] args){ int[] number = {1, 10, 31, 33, 37, 48, 60, 70, 80}; Scanner scanner = new Scanner(System.in); int num = scanner.nextInt(); System.out.println(binary(number,num)); } public static int binary(int[] array, int value){ int low = 0; int high = array.length - 1; int middle; while(low <= high){ middle = (low + high) / 2; if(value == array[middle]) return middle; if(value > array[middle]) low = middle + 1; if(value < array[middle]) high = middle - 1; // System.out.println(middle); } return -1; } }
(2)测试代码:
public class SearchTest{ public static void main(String[] args){ int[] number = {1, 10, 31, 33, 37, 48, 60, 70, 80}; if(Search.binary(number,1) != 0) System.out.println("test failed 1!"); else if(Search.binary(number,10) != 1) System.out.println("test failed 2!"); else if(Search.binary(number,31) != 2) System.out.println("test failed 3!"); else if(Search.binary(number,33) != 3) System.out.println("test failed 4!"); else if(Search.binary(number,37) != 4) System.out.println("test failed 5!"); else if(Search.binary(number,48) != 5) System.out.println("test failed 6!"); else if(Search.binary(number,60) != 6) System.out.println("test failed 7!"); else if(Search.binary(number,70) != 7) System.out.println("test failed 8!"); else if(Search.binary(number,80) != 8) System.out.println("test failed 9!"); else if(Search.binary(number,0) != -1) System.out.println("test failed 10!"); else if(Search.binary(number,40) != -1) System.out.println("test failed 11!"); else if(Search.binary(number,100) != -1) System.out.println("test failed 12!"); else System.out.println("test passed!"); } }
-
运行(测试)结果:
-
结论:二分法之前也接触过,这次将其运用到了Java程序中。根据毕老师的视频,学到了自定义函数的一些皮毛,就尝试着用函数的功能编写了一个可以查找数组中某个数的方法。其实这一点跟C语言中的知识比较类似,同一个类中,最多只允许一个main函数,它是代码的入口,执行代码先找main函数,先执行main函数;自定义的函数也和原来学的差不多,注意形参的类型个数,注意函数类型、有无返回值等情况。
其他
- 之前就听说过面向过程、面向对象,但不知道具体指的是什么。经过这一章的学习,要把面向对象这个观念牢记心中,这是区别C语言等其它面向过程语言的不二法宝。
- 对于对象的理解还要更加加深理解,要在平时的编程练习中巩固加强。熟能生出百巧来,只有熟练了,才能提高自己的编程技术,理清自己的编程思路,升华自己的编程思想。