《Java程序设计》第四章

时间:2023-02-19 22:04:18

20145221《Java程序设计》第四章-认识对象 总结

教材学习内容总结

类与对象

  • 定义:对象是Java语言中重要的组成部分,之前学过的C语言是面向过程的,而Java主要是面向对象的。Java中变量有2种类型,一个是基本类型,另一个则是类类型。使用Java撰写程序几乎都是在使用对象,要产生对象必须先定义类,类是对象的设计图,对象是类的实例。
  • 特点:
  1. 有别于C语言的程序编写,在用Java编写中,如果需要什么功能,我们就可去找一个对象,而这个对象就包含这个功能,然后通过new建立对象,通过“.”来调用该类的一些功能。
  2. 其中要能理解对象的含义,例如Clothes c1 = new Clothes();,其中c1是在栈内存中产生,而对象c1则是产生在堆内存中,c1可以近似看成指向堆内存中的指针。
  3. 书上提供了一个很好的方法正确理解其中的本质,那就是画图,形象又直观,所以在涉及对对象的理解时,一是可以画图,二是可以把相关代码贴在电脑上运行一下。还有一些常用的标准类Scanner,BigDecimal等都大大方便了我们编程过程。
  • 注意:对象相等性,首先要明白对于类类型的变量来说==和基本类型中的==有本质上的区别,因为是类类型,所以==表示的是这个类产生的2个对象是否是同一个对象,如果是同一个对象,那么==的返回值才是true,否则为false;如果想要比较2个对象的内含值,应该要用a.equals(b);。其实理解其最好的方法就是画图,例如课本P89页中的代码,以及后面相关知识的介绍,都是通过画图来理解的。

基本类型打包器

  • 概述:在上一章已经学习了基本类型的变量,但是对于Java程序语言来说,基本类型的变量效率往往不高效,Java的特点在于面向对象,所以我们也可以把这些基本类型的变量打包成对象之中,这样我们就可以像操作对象那样操作这些原本是“基本类型”的变量了。
  • 特点:
  1. 开始在学习这一节内容的时候,不能理解,明明是基本类型了,为什么还要把大费周章地又打包成类类型。在最后编写本章的操作题时,我就明白了,编写Java时一定要把固有的“面向过程”的思想转化为“面向对象”,对象可以提供我们许多功能,简化我们的编程,这在后续学习中会更加明白。
  2. 除了打包,J2SE 5.0之后,还能自动装箱、自动拆箱,在我的理解看来,就是不需要严格的像一般建立对象那样,可以简便一些(自动装箱与拆箱的功能事实是编译程序蜜糖),例如:

    Integer wrapper = 10;    // 自动装箱
    int foo = wrapper; // 自动拆箱
  • 注意:既然打包为了对象,当然也要满足对象的特点,尤其是判断“相等”。作为编程者,一定要弄明白我们的目的。在建立对象之后,如果是想比较这2个对象的内含值,则一定一定要使用a.equals(b);这种形式的比较方式,只要把握了这一点,就可以避免课本P97这样的错误。

数组对象

  • 概述:数组在Java中就是对象,牢牢把握这个概念。
  • 特点:对象的一些特点性质都可以在数组中使用。定义数组的方法,如果知道是哪些具体的数,则可以如课本P95一样,不知道具体的数可以像P98一样。在定义二维数组时,也可以仿照一维数组进行定义,总之,数组就是对象,这是数组最大的特点。
  • 注意:
  1. 既然数组是对象,那么对象需要注意的性质,数组都要注意。对于数组本身来说,不能超过其索引范围,不然会报错:ArrayIndexOutOfBoundsException(编译时不会报,运行时会报错)。
  2. 注意各种类型的数组初值情况(课本P98)。
  3. 再就是要理解二维数组的本质,二维数组,其实是在数组的基础上对于每个元素,再建立一个数组(对象),只是在很多编程实例中体现出了“二维”、“矩阵”等形式,本质还是在数组的每个元素上再建立数组。认识到这一点,加上课本的图4.5、4.6、4.7、4.8理解起来就容易多了。
  4. 数组的复制,首先由2个方法可以用System.arraycopy()Arrays.copyof(),可以通过这些类快速复制一个数组,不过在调用时要注意括号中参变量的含义,类型内容都要一致。在使用了上述方法后,要明白一点我们进行的都是“浅层复制”,是没有连同对象一起复制的。如果想要深层复制,则需自己操作,敲代码完成自行复制元素,如课本P106。

字符串对象

  • 概述:字符串本质是打包字符数组的对象,是java.lang.String类的实例。
  • 特点:既然是对象,当然也会有很多功能,length(),charAt(),toUpperCase()等。通过一些方法Byte.parseByte(number)等还可以将字符串剖析为基本类型。
  • 注意:
  1. 字符串池:如果直接将一串字符指定给2个字符串变量,则这2个字符串变量会参考到同一对象。因为在Java中,为了效率考虑,只要""包括的字符串内容相同,无论在程序代码中出现多少次,JVM都只会建立一个String实例,并在字符串池中维护。
  2. 不可变动字符串:必须知道的是:在Java中,字符串对象一旦建立,就无法更改对象中的任何内容,对象上没有任何一个方法可以更改字符串内容。使用+字符可以达到这样的效果,不过根据反编译的过程,可以发现,实际上是产生了新的字符串对象。而大量的产生新对象又是不希望看到的,所以我们可以用StringBuilder来改善,它的特点在于每次调用完后都会返回原有的StringBuilder对象,这样可以避免产生多个对象。

查询Java API文件

  • 概述:从本章的实际操作题和课本上的范例程序中,都会发现每个程序几乎都使用了不同的类,如java.util.Scanner、java.math.BigDecimal、基本类型打包器等等。在以后的编程中,如果我们想用某一个类的某一功能,但不知道如何调用,或者想了解一下某一个类具体有哪种功能等内容,就必须要通过查询Java API文件了。
  • 方法:
  1. 通过Java官方网站
  2. 直接通过搜索引擎搜索相应的类,就会显示对应的文件说明了;
  3. 以上2种方法都是在线查询Java API文件,还可以离线查询。在Windows下可以下载CHM格式的Java API。如图,这样会更加的方便。
    《Java程序设计》第四章

教材学习中的问题和解决过程

  1. 其实这一章内容开始不是很好理解,因为之前对对象没有一个概念。所以学的有点慢,但是按照老师说的方法,一个是认真看课本,另一个就是学编程必须要养成的习惯,勤敲代码。可能开始对书上给的一些代码,还不能理解,面对书上画的图,也不能透彻明白。但是好记性不如烂笔头,编程也一样,只要把这些代码敲一遍,编译运行一编,看看结果,这样印象可能会深一点,对于代码的领悟可能更也更好一些。
  2. 在最开始接触对象时,有一种感觉就是感觉“类”有点似曾相识,感觉和C语言中的结构体很像。不过我知道C语言是面向过程的,Java是面向对象的,所以我认为结构体和类还是应该有本质区别的。翻开原来的C语言书,发现结构体好像只是把不同类型的变量打包在了一个“新的类型变量中”,并不能在结构中定义功能,而类中还可以构造函数。类的功能应该更强大更灵活。
  3. 按照老师的指导,对书中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). 运行结果:
    第一种:
    《Java程序设计》第四章

    第二种:
    《Java程序设计》第四章

    第三种:
    《Java程序设计》第四章

    (3). 结论:通过上述操作,确实可以发现第三种的效率最高。

代码调试中的问题和解决过程

课后操作题

Fibonacci数列:

  1. 代码:

    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();
    */
    }
    }
  2. 运行结果:
    《Java程序设计》第四章

  3. 结论:第一次编译该程序的时候出现了乱码,可能跟我这个代码要打印汉字有关吧。经过查找了相关资料,只要在编译时输入-encoding utf-8即可解决。斐波那契数列不算难,不断迭代就可解决。程序中被注释的代码是最开始编的,最后想到也可以用一个for循环解决,不过感觉2个for循环看起来结构更清楚一些,可读性更强一些。

洗牌:

  1. 代码:

    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();
    }
    }
    }
  2. 运行结果:
    《Java程序设计》第四章

  3. 结论:拿到这个题目,想了一会,考虑怎样才能让52张扑克牌随机输出。最后想到了可以用随机数产生的方法,0-51个随机数对应的其实就是“扑克牌数组”的角标,因为角标的随机,实现了出牌的随机。当然因为编写不熟悉,输出的扑克牌有重复的,为了能更加看清楚随机数(不重复)是否成功产生,打印了52个随机数(代码中被注释的片段)。最后发现过然是随机数产生有问题,逆推回去,发现了第二个for循环中判断的条件原来写的是“numbers[j]!=0”,当时的想法是如果碰到0了,说明已经搜索完了(int型数组默认赋初值为0),不用往后搜了,但是忽略了一点,产生的随机数也含有0,如果采取这样的方法判断,就会出现重复的情况。所以在前面加了一句,将数组中的元素赋初值为1。最后打印时发现,“梅10”“心10”等这4张牌多占一个字符的宽度,所以为了打印美观,将输出格式控制为%-4s

排序:

  1. 代码:

    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();
    }
    }
  2. 运行结果:
    《Java程序设计》第四章

  3. 结论:排序题其实在C语言中也接触了不少,代码中被注释的片段用到的就是冒泡排序,可以说比较简单高效。但因为Java面向对象的特性,可以用Arrays.sort(number);,一句代码,直接将原来的数组从小到大排列,更加简单。既然再学Java,就要多使用对象,这样可以提高编程技能和效率。

查询:

  1. 代码:
    (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!");
    }
    }
  2. 运行(测试)结果:
    《Java程序设计》第四章

  3. 结论:二分法之前也接触过,这次将其运用到了Java程序中。根据毕老师的视频,学到了自定义函数的一些皮毛,就尝试着用函数的功能编写了一个可以查找数组中某个数的方法。其实这一点跟C语言中的知识比较类似,同一个类中,最多只允许一个main函数,它是代码的入口,执行代码先找main函数,先执行main函数;自定义的函数也和原来学的差不多,注意形参的类型个数,注意函数类型、有无返回值等情况。

其他

  • 之前就听说过面向过程、面向对象,但不知道具体指的是什么。经过这一章的学习,要把面向对象这个观念牢记心中,这是区别C语言等其它面向过程语言的不二法宝。
  • 对于对象的理解还要更加加深理解,要在平时的编程练习中巩固加强。熟能生出百巧来,只有熟练了,才能提高自己的编程技术,理清自己的编程思路,升华自己的编程思想。

参考资料