百木园-与人分享,
就是让自己快乐。

第2章 Java基本程序设计结构

一个简单的Java程序

//  HelloJava.java
public class HelloJava {
    public static void main(String[] args) {
        System.out.println(\"Hello Java!\");
    }
}
  • Java是区分大小写的
  • 关键字public称为访问修饰符,这些修饰符用于控制程序的其他部分对这段代码的访问级别
  • Java类名的标准命名规范:骆驼命名法。类名以大写字母开头。如果名字由多个单词组成,每个单词的第一个字母都应该大写。
  • 源代码的文件名必须与公共类的名字相同,并用.java作为扩展名。因此存储上面这段代码的文件名必须是HelloJava.java

使用命令行工具编译运行Java程序

  • java -version:查看Java SE的版本信息
  • javac HelloJava.java编译.java源文件,生成后缀为.class的字节码文件
  • java HelloJava使用Java解释器将字节码文件翻译成机器代码

Java程序的执行过程

Java程序的运行必须经过编写、编译和运行3个步骤

  1. 编辑:是指在Java开发环境中进行程序代码的输入,最终形成后缀名为.java的Java源文件。
  2. 编译:是指使用Java编译器对源文件进行错误排査的过程,编译后将生成后缀名为.class的字节码文件,不像C语言那样生成可执行文件。
  3. 运行:是指使用Java解释器将字节码文件翻译成机器代码,执行并显示结果。

什么是字节码文件?

字节码文件是一种和任何具体机器环境及操作系统环境无关的中间代码。它是一种二进制文件,是Java源文件由Java编译器编译后生成的目标代码文件。编程人员和计算机都无法直接读懂字节码文件,它必须由专用的Java解释器来解释执行,因此Java是一种在编译基础上进行解释运行的语言。

Java解释器负责将字节码文件翻译成具体硬件环境和操作系统平台下的机器代码,以便执行。因此Java程序不能直接运行在现有的操作系统平台上,它必须运行在被称为Java虚拟机的软件平台之上。

Java虚拟机(JVM)是运行Java程序的软件环境,Java解释器是Java虚拟机的一部分。在运行Java程序时,首先会启动JVM,然后由它来负责解释执行Java的字节码程序,并且Java字节码程序只能运行于JVM之上。这样利用JVM就可以把Java字节码程序和具体的硬件平台以及操作系统环境分隔开来,只要在不同的计算机上安装了针对特定平台的JVM,Java程序就可以运行,而不用考虑当前具体的硬件平台及操作系统环境,也不用考虑字节码文件是在何种平台上生成的。

JVM把这种不同软、硬件平台的具体差别隐藏起来,从而实现了真正的二进制代码级的跨平台移植。JVM是Java平台架构的基础,Java的跨平台特性正是通过在JVM中运行Java程序实现的。Java的这种运行机制可通过下图说明:

Java语言这种“一次编写,多端运行”的方式,有效地解决了目前大多数高级程序设计语言需要针对不同系统来编译产生不同机器代码的问题,即硬件环境和操作平台的异构问题,大大降低了程序开发、维护和管理的开销。

Java程序通过JVM可以实现跨平台特性,但JVM是不跨平台的。也就是说,不同操作系统之上的JVM是不同的,Windows平台之上的JVM不能用在 Linux平台,反之亦然。


注释(Comments)

// 行注释
/*
块注释
*/

JDK包含一个很有用的工具,叫做javadoc,它可以由源文件生成一个HTML文档

/**

 */
/**
 *
 *
 */

上面两种格式都是合法的,第二种是大部分IDE会提供的格式。

在文档注释的第一句应该是概要性的句子。javadoc工具自动将这些句子抽取出来生成概要页

类注释必须放在import语句之后,类定义之前。每个方法注释必须放在所描述的方法之前。

常用的javadoc标记

  • @author:指定Java程序的作者。
  • @version:指定源文件的版本。
  • @deprecated:不推荐使用的方法。
  • @param:方法的参数说明信息。
  • @return:方法的返回值说明信息。
  • @see:“参见”,用于指定交叉参考的内容。
  • @exception:抛出异常的类型。
  • @throws:抛出的异常,和@exception同义。

需要指出的是,这些标记的使用是有位置限制的。上面这些标记可以出现在类或者接口文档注释中的有@see、@deprecated、@author、@version等;可以出现在方法或构造器文档注释中的有@see、@deprecated、@param、@return、@throws和@exception等;可以出现在成员变量的文档注释中的有@see和@deprecated等。


关键字(Keywords)

Java语言中有一些具有特殊用途的单词被称为关键字(keywords),当定义标识符时,不要让标识符和关键字相同,否则将引起错误。

abstract continue for new switch
assert default if packa synchronized
boolean do goto private this
break double implements protected throw
byte else import public throws
case enum instanceof return transient
catch extends int short try
char final interface static void
class finally long strictfp volatile
const float native super while

其中goto和const这两个关键字也被称为保留字,意思是,Java现在还未使用这两个关键字,但可能在未来的Java版本中使用这两个关键字。

以下字符序列不能用作关键字:

  • true、false不能用作关键字,它们是boolean型字面量
  • null不能用作关键字,它是null字面量
  • var也不是关键字,而是作为标识符和lambda形式参数

数据类型

Java是一种强类型语言。这意味着必须为每一种变量声明一种类型。Java中支持的数据类型分为两类:基本类型(Primitive Type)引用类型(Reference Type)

基本类型包括boolean类型和数值类型。数值类型有整数类型和浮点类型。整数类型包括byte、short、int、long、char,浮点类型包括float和double

引用类型包括接口数组类型,还有一种特殊的null类型。所谓引用数据类型就是对一个对象的引用,对象包括实例和数组两种。

空类型(null type)就是null值的类型,这种类型没有名称。因为null类型没有名称,所以不可能声明一个null类型的变量或者转换到null类型。空引用(null)是null类型变量唯一的值。空引用(null)可以转换为任何引用类型。

💡 在 Java 中 boolean 类型占多少字节 | Binkery 技术博客

基本类型 位数 字节 默认值 取值范围 其他
byte 8 1 0 -128~127
short 16 2 0 -32768~32767
int 32 4 0 2147483648 ~ 2147483647
long 64 8 0L 9223372036854775808 ~ 9223372036854775807 初始化long型变量,须后加‘l’或‘L’
char 16 2 \'u0000’ 0 ~ 65535
float 32 4 0f 1.4E-45 ~ 3.4028235E38 初始化float型变量,须后加‘f’或‘F’
double 64 8 0d 4.9E-324 ~ 1.7976931348623157E308
boolean 1 false true、false

Java语言支持一些特殊的转义字符序列。

\\n 换行 (0x0a)
\\r 回车 (0x0d)
\\f 换页符(0x0c)
\\b 退格 (0x08)
\\0 空字符 (0x0)
\\s 空格 (0x20)
\\t 制表符
\" 双引号
\' 单引号
\\ 反斜杠
\\ddd 八进制字符 (ddd)
\\uxxxx 16进制Unicode字符 (xxxx)

类型转换

自动类型转换

类型转换.png

整型的默认类型是int,浮点数的默认类型是double。

  • 有多种类型的数据混合运算时,系统首先自动将所有数据转换成容量最大的那种数据类型,然后再进行计算。
  • byte,short,char之间不会相互转换,他们三者在计算时首先转换为int类型。
  • boolean类型不能与其它数据类型运算。
  • 当把任何基本数据类型的值和字符串(String)进行连接运算时(+),基本数据类型的值将自动转化为字符串(String)类型。

强制类型转换

double x = 9.997;
int nx = (int) x;

如果想要对浮点数进行舍入运算,以便得到最接近的整数,那就需要用到Math.round()方法:

double x = 9.997;
int nx = (int)Math.round(x);

💡 如果试图将一个数值从一种类型强制转换为另一种类型,而又超出了目标类型的表示范围,结果就会截断成一个完全不同的值。例如,(byte)300的实际值为44

流程控制

  • if语句

    if(logic expression){
        statement
    }
    
    if(logic expression){
    	statement
    }else{
    	statement
    }
    
    if(logic expression){
    	statement
    }else if{
    	statement
    }else if{
        statement
    }
    ...
    else{
    
    }
    
  • switch语句

    switch (expression){
        case condition1:
            {
                statement(s)
                break;
            }
        case condition1:
            {
                statement(s)
                break;
            }
        ...
        case conditionN:
            {
                statement(s)
                break;
            }
        default:
            statement(s)
    }
    
  • while循环

    [init_statement]
    while(test_expression){
        statement;
        [iteration_statement]
    }
    
  • do-while循环

    [init_statement]
    do{
        statement;
        [iteration_statement]
    }while(test_expression)
    

    do-while循环与while循环的区别在于:while循环是先判断循环条件,如果条件为真则执行循环体;而do-while循环则先执行循环体,然后才判断循环条件,如果循环条件为真,则执行下一次循环,否则终止循环。

  • for循环

    for(init_statement;test_expression;iteration_statement){
        statement
    }
    

    程序执行for循环时,先执行循环的初始化语句init_statement,初始化语句只在循环开始前执行一次。每次执行循环体之前,先计算test_expression循环条件的值,如果循环条件返true,则执行循环体,循环体执行结束后执行循环迭代语句。因此,对于for循环而言,循环条件总比循环体要多执行一次,因为最后一次执行循环条件返回false,将不再执行循环体。

  • for-each循环

    for(Type var:array){
       statement
    }
    
  • break

    💡 break用于switch语句中,终止switch语句break用于循环时,跳出循环

    public class BreakTest {
        public static void main(String[] args) {
    
            for(int n = 0; n < 3; n++){
                if(n == 1){
                    break;
                }
                System.out.println(\"n = \" + n);
            }
    
            System.out.println(\"================\");
    
            // 嵌套循环,break在内层
            for(int i = 0; i < 3; ++i){
                for(int j = 0; j < 3; j++){
                    if(i==1){
                        break;
                    }
                    System.out.println(\"i = \"+ i +\", j = \" + j);
                }
            }
    
            System.out.println(\"================\");
    
            // 嵌套循环,break在外层
            for(int i = 0; i < 3; ++i){
                if(i==1){
                    break;
                }
                for(int j = 0; j < 3; j++){
                    System.out.println(\"i = \"+ i +\", j = \" + j);
                }
            }
        }
    }
    

    💡 在有break语句的嵌套循环中,break只能跳出当前循环。即如果break语句在外层,则会跳出整个循环;而如果break语句在内层,则只会跳出当前所在的内层循环。

  • continue

    💡 continue用在循环中,跳出本次循环,继续执行下一次循环

    public class ContinueTest {
        public static void main(String[] args) {
    
            for(int n = 0; n < 3; n++){
                if(n == 1){
                    continue;
                }
                System.out.println(\"n = \" + n);
            }
    
            System.out.println(\"================\");
    
            // 嵌套循环
            for(int i = 0; i < 3; ++i){
                for(int j = 0; j < 3; j++){
                    if(i ==1 && j == 1){
                        continue;
                    }
                    System.out.println(\"i = \"+ i +\", j = \" + j);
                }
            }
        }
    }
    

数组

Java的数组要求所有的数组元素具有相同的数据类型。既可以存储基本类型的数据,也可以存储引用类型的数据

💡 因为Java语言是面向对象的语言,而类与类之间可以支持继承关系,这样可能产生一个数组里可以存放多种数据类型的假象。例如有一个水果数组,要求每个数组元素都是水果,实际上数组元素既可以是苹果,也可以是香蕉(苹果、香蕉都继承了水果,都是一种特殊的水果),但这个数组的数组元素的类型还是唯一的,只能是水果类型。

定义数组

type[] arrayName; // 推荐使用
type arrayName[l;

数组是一种引用类型的变量,因此使用它定义一个变量时,仅仅表示定义了一个引用变量(也就是定义了一个指针),这个引用变量还未指向任何有效的内存,因此定义数组时不能指定数组的长度。而且由于定义数组只是定义了一个引用变量,并未指向任何有效的内存空间,所以还没有内存空间来存储数组元素,因此这个数组也不能使用,只有对数组进行初始化后才可以使用。

数组初始化

初始化,就是为数组的数组元素分配内存空间,并为每个数组元素赋初始值

一旦数组的初始化完成,数组在内存中所占的空间将被固定下来,因此数组的长度将不可改变。即使把某个数组元素的数据清空,但它所占的空间依然被保留,依然属于该数组,数组的长度依然不变。

数组的初始化有如下两种方式:

  • 静态初始化:初始化时由程序员显式指定每个数组元素的初始值,由系统决定数组长度。

    arrayName = new type[] {element1,element2,element3,element4 ...};
    
  • 动态初始化:初始化时程序员只指定数组长度,由系统为数组元素分配初始值。

    arrayName = new type[length];
    
    • 数组元素的类型是基本类型中的整数类型(byte、short、int、long),则数组元素的值是0。
    • 数组元素的类型是基本类型中的浮点类型(float、double),则数组元素的值是0.0。
    • 数组元素的类型是基本类型中的字符类型(char),则数组元素的值是\'\\u0000\'。
    • 数组元素的类型是基本类型中的布尔类型(boolean),则数组元素的值是false。
    • 数组元素的类型是引用类型(类、接口和数组),则数组元素的值是null。

使用数组

Java语言的数组索引是从0开始。数组最常用的用法就是访问数组元素,包括对数组元素进行赋值和取出数组元素的值。访问数组元素都是通过在数组引用变量后紧跟一个方括号[],方括号里是数组元素的索引值

所有的数组都提供了一个length属性,通过这个属性可以访问到数组的长度,一旦获得了数组的长度,就可以通过循环来遍历该数组的每个数组元素。

package com.ch04;

public class ArrayTest {
    public static void main(String[] args) {
//        定义(声明)数组
        int[] array1;
        int[] array2;

//        静态初始化
        array1 = new int[]{1, 3, 5, 7, 9};
//        动态初始化
        array2 = new int[5];

//        输出数组array1中的最后一个元素
        System.out.println(array1[array1.length-1]);  // 9

//        根据数组的length属性遍历数组array2
        for(int i = 0; i < array2.length; i++){
            System.out.print(array2[i]); // 00000 
        }
        System.out.println();

//        让array2指向array1引用的数组
        array2 = array1;

//        再次遍历数组array2
        for(int i = 0; i < array2.length;i++){
            System.out.print(array2[i]); // 13579 
        }
    }
}

从上面的输出结果也可以看出,使用动态初始化数组时,系统为int数组默认的初始化值为0

深入理解Java数组

数组引用变量只是一个引用,这个引用变量可以指向任何有效的内存,只有当该引用指向有效内存后,才可通过该数组变量来访问数组元素。

结合上方代码分析!

结合上方代码分析!

package com.ch04;
class Person{
    public int age;
    public double height;

//    定义一个info()方法
    public void info(){
        System.out.println(\"My age is \" + age + \", and my height is \" + height);
    }
}

public class ArrayTest1 {
    public static void main(String[] args){
//        定义(声明)数组,其类型是Person
        Person[] P;
//        动态初始化
        P = new Person[2];

//        创建person1对象
        Person person1 = new Person();
        person1.age = 20;
        person1.height = 188.5;

//        创建person2对象
        Person person2 = new Person();
        person2.age = 22;
        person2.height = 166.5;

//        将person1赋给第一个数组元素,person2赋给第二个数组元素
        P[0] = person1;
        P[1] = person2;
//        输出结果一样,因为P[1]和person2指向同一个Person实例
        person2.info(); // My age is 22, and my height is 166.5 
        P[1].info(); // My age is 22, and my height is 166.5 
    }
}

可以看到输出结果相同,(结合下图分析)因为P[1]和person2指向内存的同一片区域,因此通过这两种方式访问得到的结果是相同的

深入理解Java引用数组.png

操作数组的工具类:Arrays

Java提供的Arrays类里包含的一些static修饰的方法可以直接操作数组,这个Arrays类里包含了如下几个static修饰的方法(static修饰的方法可以直接通过类名调用)。

  • int binarySearch(type[] a, type key):使用二分法查询key元素值在a数组中出现的索引;如果a数组不包含key元素值,则返回负数。调用该方法时要求数组中元素已经按升序排列,这样才能得到正确结果。
  • int binarySearch(type[] a, int fromIndex, int toIndex,type key):这个方法与前一个方法类似,但它只搜索a数组中fromIndex到toIndex索引的元素。调用该方法时要求数组中元素已经按升序排列,这样才能得到正确结果。
  • type[] copyOf(type[] original, int length):这个方法将会把original数组复制成一个新数组,其中length是新数组的长度。如果length小于original数组的长度,则新数组就是原数组的前面length个元素;如果length大于original数组的长度,则新数组的前面元素就是原数组的所有元素,后面补充0(数值类型)、false(布尔类型)或者null(引用类型)。
  • type[] copyOfRange(type[] original, int from, int to):这个方法与前面方法相似,但这个方法只复制original数组的from索引到to索引的元素。
  • boolean equals(type[] a, type[] a2):如果a数组和a2数组的长度相等,而且a数组和a2数组的数组元素也一一相同,该方法将返回true。
  • void fill(type[] a, type val):该方法将会把a数组的所有元素都赋值为val。
  • void fill(type[] a, int fromIndex, int toIndex, type val):该方法与前一个方法的作用相同,区别只是该方法仅仅将a数组的fromIndex到toIndex索引的数组元素赋值为val。
  • void sort(type[] a):该方法对a数组的数组元素进行排序。
  • void sort(type[] a, int fromIndex, int toIndex):该方法与前一个方法相似,区别是该方法仅仅对fromIndex到toIndex索引的元素进行排序。
  • String toString(type[] a):该方法将一个数组转换成一个字符串。该方法按顺序把多个数组元素连缀在一起,多个数组元素使用英文逗号(,)和空格隔开。

Java 8增强了Arrays类的功能,为Arrays类增加了一些工具方法,这些工具方法可以充分利用多CPU并行的能力来提高设值、排序的性能。下面是Java 8为Arrays类增加的工具方法。

  • void parallelPrefix(xxx[] array, XxxBinaryOperator op):该方法使用op参数指定的计算公式计算得到的结果作为新的数组元素。op计算公式包括left、right两个形参,其中left代表新数组中前一个索引处的元素,right代表array数组中当前索引处的元素。新数组的第一个元素无须计算,直接等于array数组的第一个元素。
  • void parallelPrefix(xxx[] array, int fromIndex, int toIndex, XxxBinaryOperator op):该方法与上一个方法相 似,区别是该方法仅重新计算fromIndex到toIndex索引的元素。
  • void setAll(xxx[] array, IntToXxxFunction generator): 该方法使用指定的生成器(generator)为所有数组元素设置值,该生成器控制数组元素的值的生成算
  • void parallelSetAll(xxx[] array, IntToXxxFunction generator):该方法的功能与上一个方法相同,只是该方法增加了并行能力,可以利用多CPU并行来提高性能。
  • void parallelSort(xxx[] a):该方法的功能与Arrays类以前 就有的sort()方法相似,只是该方法增加了并行能力,可以利用多CPU并行来提高性能。
  • void parallelSort(xxx[] a, int fromIndex, int toIndex):该方法与上一个方法相似,区别是该方法仅对fromIndex到toIndex索引的元素进行排序
  • Spliterator.OfXxx spliterator(xxx[] array):将该数组的所有元素转换成对应的Spliterator对象。
  • Spliterator.OfXxx spliterator(xxx[] array, int startInclusive, int endExclusive):该方法与上一个方法相 似,区别是该方法仅转换startInclusive到endExclusive索引的元素。
  • XxxStream stream(xxx[] array):该方法将数组转换为Stream,Stream是Java 8新增的流式编程的API。
  • XxxStream stream(xxx[] array, int startInclusive, int endExclusive):该方法与上一个方法相似,区别是该方法仅将fromIndex到toIndex索引的元素转换为Stream。

上面方法列表中,所有以parallel开头的方法都表示该方法可利用CPU并行的能力来提高性能。上面方法中的xxx代表不同的数据类型,比如处理int[]型数组时应将xxx换成int,处理long[]型数组时应将xxx换成long。


来源:https://www.cnblogs.com/gengduc/p/16073488.html
本站部分图文来源于网络,如有侵权请联系删除。

未经允许不得转载:百木园 » 第2章 Java基本程序设计结构

相关推荐

  • 暂无文章