【Java核心计算 基础知识(第9版)】第3章 Java的基本程序设计结构

本章要点
- 一个简单的Java应用程序
- 注释
- 数据类型
- 变量
- 运算符
- 字符串
- 输入输出流
- 控制流
- 大数值
- 数组


3.1 一个简单的Java应用程序

 

上图是一个最简单的Java程序,它表示:声明一个名为FirstSample的public类,当运行这个类时,JVM会调用类中的main方法,该方法向控制台打印“Hello, world!”这么一句话。麻雀虽小,但是Java程序的基本要素都包含在里面。

1)Java对大小写敏感
- 这意味着,对Java而言,Main和main是两个不同的名词。

2)public:访问修饰符
- 它用于控制程序的其他部分对这段代码的访问权限,有public,protected,default,private四种。

3)class:声明一个类
- class用于声明一个类。可以将类视为一个加载程序逻辑的容器,程序逻辑定义了应用程序的行为。
- 类是构建所有Java应用程序的构建块。
- 一个源文件(.java文件)最多只能包含一个public类,且源文件与该类名字必须相同。
- enum用于声明一个枚举,声明方法不需要特定的标识符。

4)编译与运行
- 确保已安装JDK且配置环境变量。
- 编译:javac FirstSample.java,即javac + 空格 + 源文件名(含扩展名.java),可不区分大小写(因Windows不区分大小写)。
- 运行:java FirstSample,即java + 空格 + 程序名(不含扩展名.class),严格区分大小写。
- 运行程序时,JVM将从public类中的main方法开始执行,这里就是程序的入口。所以一个源文件中只能包含一个public类,这个类中只能有一个main方法,这个方法必须拥有最高的访问权限(public)。

5)代码块与语句
- 用{}来区分程序的各个部分,如类与类,方法与方法,局部代码块等。{为该部分的起点,}为该部分的结束。
- 在一对{}之间声明的标识符只在这部分有效。
- “语句”表示一个句子,可视为代码的基本单元,必须以;结束。

6)对成员的调用
- 成员指类的方法和变量
- .用于表示对成员的调用,如object.method(param)表示调用一个方法,object.variable表示调用一个变量。


3.2 注释

  • 注释不会出现在编译后的可执行程序中,它用于程序员对所写代码的说明,如这段代码为什么要这么写,别人调用我的接口需要注意什么,这个变量是用来做什么的等。
  • Java有三种注释。
    • // 单行注释:注释本行。
    • /* */ 多行注释:注释被包裹的内容。
    • /** */ 文档注释:注释被包裹的内容,并且可以用javadoc命令自动生成文档。

3.3 数据类型

  • Java是一种强类型语言,即每个变量必须声明一种类型,一旦声明之后不可以改变。

  • 8种基本数据类型:

类型存储需求最大值最小值示例备注
long 8 Byte 263 2631 123456789L  
int 4 Byte 231 2311 (约21亿) 123456789 整数默认类型
short 2 Byte 215 2151 (32767) 12345  
byte 1 Byte 27 271(127) 123  
double 8 Byte 约-1.798E+308 约1.798E+308(21024 123456789.0 小数默认类型,有效位数15位
float 4 Byte 约-3.4E+38F 约3.4E+38F (2128 123456789.0F 有效位数6~7位
char 2 Byte 0 (\u0000) 65535 (\uffff) ‘A’ 转换为数值时无负值
boolean       true/false 无法转换为其他类型

注1:double和float计算遵循IEEE754规范。
注2:三个特殊值常量
- 正无穷 Double.POSITIVE_INFINITY,如 1/0.0
- 负无穷 Double.NEGATIVE_INFINITY,如 -1/0.0
- 非数值 Double.NaN,如 0/0.0
- 所有”非数值“均不相等,检测非数值只能用Double.isNaN(x)

  • java中的进制表示

    • 十进制 1234567890
    • 十六进制 0x123ABCDEF
    • 八进制 012345678
    • 二进制 0b00001111
  • 十进制转换为二进制:除以2取余 ,直到商为0,然后将余数倒序排列

     

  • 二进制转换为十进制:各位乘以2n1并求和
    如 110: 122+121+020=4+2+0=6

  • 负数的二进制表现形式:绝对值得二进制形式各位取反,然后+1

     

  • 引用数据类型: class, enum, interface, array


3.4 变量

在Java中,每一个变量属于一种类型。在声明变量时,变量所属的类型位于变量名之前。
如: int counter;
double salary;

3.4.1 变量初始化

  • 声明一个变量之后,必须用赋值语句对变量进行显式初始化(原文如此,这里应特指局部变量),变量被初始化之前不能使用。
  • 变量的赋值和使用:
int i;
i = 20;
i = 30;

String str = "abc";
str = str + "def";

3.4.2 常量

  • 常量用关键字”final”指示,表示这个变量只能被赋值一次,即一旦被赋值之后,就不能再更改。
  • 声明为final的成员变量无默认值,必须显式初始化。
  • 常量的赋值和使用:
class Demo{
    static final int sCounter = 0; // 静态常量必须在声明时显式初始化
    final int iSalary = 25000; // 非静态的常量可在声明时初始化或在构造方法中初始化
    final int iDays;

    Demo(){
        iDays = 10;
    }
}

3.5 运算符

  • +, -, *, /, % : 加、减、乘、除、取余

3.5.1 自增运算符与自减运算符

  • ++, –
  • ++i : 先进行加1运算,再参与其他运算;
  • i++ : 先参与其他运算,再进行加1运算;
  • 因容易混淆,并且可能产生bug(曾听说过在某些版本的JDK下向控制台打印i++与++i输出同样的值),不建议在其他表达式内部使用自增/自减运算;
int i = 1;
int j = 1;
int k = i++ * j++; // 不建议使用
System.out.println(++i); // 2
System.out.println(j++); // 1

3.5.2 关系运算符与boolean运算符

  • ==, != :相等,不相等(用于基本类型时判断字面值,用于引用类型时判断引用地址)
  • <, <=, >, >=
  • &, &&, |, ||
  • 三元操作符?:,如String str = 1 == 2 ? “1等于2” : “1不等于2”;

3.5.3 位运算符

  • 在处理整型数值时,可以直接对组成整型数值的各个位进行操作。
  • &, |, ^, ~, >>, >>>, << 与,或,异或,非,右移,无符号右移,左移
  • 应用例子:用一个变量的每个二进制位各代表一个flag:
class Test{
    int mod1 = 0b1;
    int mod2 = 0b10;
    int mod3 = 0b100;
    int mod4 = 0b1000;
    int mod5 = 0b10000;
    int mod6 = 0b100000;
    int mod7 = 0b1000000;
    int mod8 = 0b10000000;
    boolean isMod1(int mod){
        return ((mod & mod1) != 0) ? true : false;
    }
    boolean isMod2(int mod){
        return ((mod & mod2) != 0) ? true : false;
    }
    ...
}

3.5.4 数学函数与常量

  • 请查阅java.lang.Math类

3.5.5 数值类型之间的转换

  • byte, short, char三种类型之间的运算自动提升为int
  • int -> long -> float -> double,自动向上转型

3.5.6 强制类型转换

  • 转换方式:(targerType) variable。
  • 通过截断小数部分而不是四舍五入将浮点值转换为整型。
  • 当变量超过目标类型的表示范围时,会根据补码截断,而不是保留目标类型的最大/最小值。

3.5.7 括号与运算符级别

优先级运算符结合性
1 [] . ()(方法调用) 从左向右
2 ! ~ ++ – +(正号) -(负号) ()(强制类型转换) new 从右向左
3 */% 从左向右
4 +- 从左向右
5 << >> >>> 从左向右
6 < <= > >= instanceof 从左向右
7 == != 从左向右
8 & 从左向右
9 ^ 从左向右
10 | 从左向右
11 && 从左向右
12 || 从左向右
13 ?:(三元运算符) 从右向左
14 = += -= *= /= %= &= |= ^= <<= >>= >>>= 从右向左

3.5.8 枚举类型

  • 枚举类型包括有限和命名的值。如 enum Size{S,M,L,XL,XXL,XXXL}
class Demo{
    enum Size{
        S,M,L,XL,XXL,XXXL
    }

    void test(){
        Size s = Size.S;
    }
}

3.6 字符串

  • 从概念上讲,Java字符串就是Unicode字符序列。Java没有内置的字符串类型,而是在标准Java类库中提供了一个预定义类String,每个用”“括起来的字符串都是String类的一个实例。

3.6.1 子串

  • 一个字符串中“一段”称为它的一个子串。
  • 空串”“是任何字符串的子串。
  • String类的substring(a,b)方法可以从截取子串,截取范围为[a,b)。contains(str)方法判断是否包含该子串。

3.6.2 拼接

  • 可以使用+号拼接任意两个字符串。
  • 当将一个字符串与任意非字符串的值拼接时,后者被转换为字符串(基本类型根据字面值转换,引用类型根据toString()方法转换)。
public class Demo {
    public static void main(String[] args) {
        Demo1 demo1 = new Demo1();
        Demo2 demo2 = new Demo2();
        System.out.println(demo1 + ""); // 输出demo1的引用地址
        System.out.println(demo2 + ""); // 输出“Demo”
    }
}

class Demo1{

}

class Demo2{
    @Override
    public String toString() {
        return "Demo";
    }
}

3.6.3 不可变字符串

  • 由于不能修改Java字符串中的字符,所以在Java文档中将String类对象称为不可变字符串。例如,”Hello”永远包含字符H,e,l,l,o的代码单元序列,一旦改变,就不再是”Hello”。
  • 字符串不可改变,但字符串变量所引用的字符串对象是可以改变的。
  • 一个经典的面试题:String s = new String(“abc”)共创建立多少个对象。
    • String s: 一个String类型的变量,位于stack区。
    • “abc”: 一个String类型对象,位于PermGen区的常量池(如果池中已有字面值为”abc”的对象则不会重复创建)。
    • new String(“abc”): 一个String类型对象,位于heap区。
  • String类中用于存储对象的值的变量使用了final修饰,只能在构造时赋值一次。即,一个String对象所记的value永远不能改变,这就是不可变字符串的代码实现。

  • 我对这里有一个疑惑
    书中表示“原始字符串放置在堆中”,可以自动回收。但是,根据我查阅的其他资料,原始的字符串”abc”应该位于方法区,方法区在主程序运行期间是不会自动回收的。这里似乎有矛盾。
    简单看了下《深入理解Java虚拟机》:Java虚拟机规范把方法区描述为堆的一个逻辑部分,但是它却有一个别名叫做“非堆”,目的应该是与Java堆区分开来。也就是说,方法区应该是堆的一部分,但方法区的实现属于虚拟机实现细节,不受虚拟机规范约束。为防止内存溢出以及极少数方法在不同虚拟机下有不同的表现,目前倾向于不将方法区实现为“永久代”,但需要为方法区单独编写内存管理代码。由于对这一块了解较少,这个问题尚待深入了解。

private final char value[];

public String() {
    this.value = "".value;
}

public String(String original) {
    this.value = original.value;
    this.hash = original.hash;
}

public String(char value[]) {
    this.value = Arrays.copyOf(value, value.length);
}
...

3.6.4 检测字符串是否相等

  • 可以使用equals(Object)和equalsIgnoreCase(String)方法检测字符串是否相等(后者忽略大小写。)为避免NullPointerException,当比较常量与变量时,应该以常量调用equals()方法。
  • equals(Object)虽然形式参数被声明为Object,但实际上在比较时会先做instanceof String判断,所以实际上只能比较String类型。
        String string = "abc";
        StringBuffer sBuffer = new StringBuffer("abc");
        System.out.println(string.equals(sBuffer)); // false
    public boolean equals(Object anObject) {
        if (this == anObject) {
            return true;
        }
        if (anObject instanceof String) {
            String anotherString = (String)anObject;
            int n = value.length;
            if (n == anotherString.value.length) {
                char v1[] = value;
                char v2[] = anotherString.value;
                int i = 0;
                while (n-- != 0) {
                    if (v1[i] != v2[i])
                        return false;
                    i++;
                }
                return true;
            }
        }
        return false;
    }

3.6.5 空串与Null串

  • 空串”“是长度为0的字符串对象,可以用来调用String类的所有成员方法和属性。
  • Null串null表示字符串变量目前没有引用任何字符串对象,不能作任何调用。

3.6.6 代码点与代码单元

  • char数据类型是一个采用UTF-16编码表示Unicode代码点的代码单元。大多数常用的Unicode字符使用一个代码单元就可以表示(即一个char对应一个实际字符),而辅助字符需要一对代码单元表示(即两个char对应一个实际字符)。
  • 一个实际字符为一个代码点(codePoint)。
  • leng()和charA()方法都是基于代码单元,所以在字符串中含有辅助字符的情况下,返回的结果可能不是想要的。这时候,需要用codePointCount()和offsetByCodePonits()+codePointAt()方法代替。

3.6.7 字符串API(java.lang.String)

  • char charAt(int index)
    返回给定位置的代码单元。不建议调用这个方法获取实际字符。
  • int codePointAt(int index)
    返回从给定位置开始或结束的代码点。主要这里的index是代码单元的index,而不是代码点的index。建议使用该方法获取实际字符。
  • int offsetByCodePoints(int startIndex, int cpCount)
    返回从startIndex开始,位移cpCount代码点后的index。
  • int compareTo(String other)
  • boolean endsWith(String suffix)
  • bollean equals(Object other)
  • equalsIgnoreCase(String other)
  • int indexOf(String subString)
  • int indexOf(String subString, int fromIndex)
  • int indexOf(int cp)
  • int indexOf(int cp, int fromIndex)
  • int lastIndexOf(String subString)
  • int lastIndexOf(String subString, int fromIndex)
  • int lastIndexOf(int cp)
  • int lastIndexOf(int cp, int fromIndex)
  • int length()
  • int codePointCount(int startIndex, int endIndex)
  • String replace(CharSequence oldString, CharSequence newString)
  • boolean startsWith(String prefix)
  • String substring(int beginIndex)
  • String substring(int beginIndex, int endIndex)
  • String toLowerCase()
  • String toUpperCase()
  • String trim()
    注意:凡是(begin, end)形式的参数,对应的都是半闭合区间,即[begin, end),包含开始而不包含结束。

3.6.9 构建字符串

  • 由于String对象是不可变的,当需要用多个较短的字符串拼接成一个长字符串时,一般应使用StringBuilder或StringBuffer以提高效率。
  • StringBuilder()/StringBuffer()
  • int length()
  • StringBuilder append(String str)
  • StringBuilder append(char c)
  • StringBuilder appendCodePoint(int cp)
  • void setCharAt(int index, char c)
  • StringBuilder insert(int offset, String str)
    在offset位置插入字符串
  • StringBuilder insert(int offset, char c)
  • StringBuilder delete(int startIndex, int endIndex)
  • String toString()
  • 注意:以上返回值类型为StringBuilder的方法返回的均是调用者自身。

3.7 输入输出

3.7.1 读取输入

  • java.lang.Scanner
    • Scanner(InputStream in)
    • String nextLine()
    • String next()
    • int nextInt()
    • double nextDouble()
    • boolean hasNext()
    • boolean hasNextInt()
    • boolean hasNextDouble()
  • java.lang.System
    • static Console console()
  • java.io.Console
    • char[] readPassword(String prompt, Object… args)
      提供一个格式化提示,然后从控制台读取密码,禁用回显。
    • String readLine(String prompt, Object… args)
      提供一个格式化提示,然后从控制台读取单行文本。

3.7.2 格式化输出

  • System.out.print(Object obj)
  • System.out.printf(String format, Object… args)

3.7.2 文件输入与输出

  • java.util.Scanner
    • Scanner(File f)
    • Scanner(Path p)
    • Scanner(String data)
  • java.io.PrintWriter
    • PrintWriter(String fileName)
  • java.nio.file.Paths
    • static Path get(String pahtName)

3.8 控制流程

3.8.1 块作用域

  • 块(即复合语句)是指由一对花括号括起来的若干条简单的Java语句。块确定了变量的作用域。一个块可以嵌套在另一个块中。
  • 不能再嵌套的两个块中声明同名的变量。

3.8.2 条件语句

if(condition){
    statement; // 如果condition为true,执行statement;否则什么都不执行。 
}
if(condition){
    statement1; // 如果condition为true,执行statement1;
}else{
    statement2; // 否则,执行statement2。
}
if(condition1){
    statement1; // 如果condition1为true,执行statement1;
}else if(condition2){
    statement2; // 如果condition2为true,执行statement2;依次类推;
}...
else{
    statement(n+1); // 如果所有condition都为false,执行statement(n+1)。
}

3.8.3 循环语句

while(condition){
    statement; // 循环判断condition,当为true时执行statement;当为false时停止循环。
}
do{
    statement; // 执行statement,然后循环判断condition,当为true时执行statement;当为false时停止循环。
}while(condition)

3.8.4 确定循环

for(initialize counter; condition; update counter){
    statment; // 执行initialize counter,然后循环判断condition,当为true时依次执行statement及update counter;当为fasle时停止循环。
}

3.8.5 多重选择:switch语句

switch(key){    // 可以是char,byte,short,int,enum,String(1.7)。
case value1:
    statement1; // 当key的值等于value1时,执行statement1。
    break; // break或}表示退出switch语句。
case value2:
    statement2; // 当key的值等于value2时,执行statement2,依次类推。
    break;
...
default:
    statement(n+1); // 当key的值不等于任何value,执行statement(n+1)。
    break;
}

3.8.6 中断控制流程语句

  • break:可用于循环语句或switch语句。跳出循环语句或switch语句,不再执行剩余部分(停止循环)。
        A: for(int i = 0; i <= 9; i ++){
            B: for(int j = 0; j <= 9; j ++){
                C: for(int k = 0; k <= 9; k ++){
                    if(k == 1){
                        break; // 当k=1时,跳出所在循环(C)
                    }
                    System.out.println("" + i + j + k);
                }
            }
        }
        A: for(int i = 0; i <= 9; i ++){
            B: for(int j = 0; j <= 9; j ++){
                C: for(int k = 0; k <= 9; k ++){
                    if(k == 1){
                        break A; // 当k=1时,跳出所在循环A
                    }
                    System.out.println("" + i + j + k);
                }
            }
        }
  • coutinue:仅用于循环语句,用法语break相同,但只跳过当次循环的剩余部分,跳转到下一次判断(while)或更新计数器(for)部分。

3.9 大数值

  • BigInteger可以表示任意精度的整数,BigDecimal可以表示任意精度的浮点数。
  • java.math.BigInteger
    • BigInteger add(BigInteger other)
    • BigInteger subtract(BigInteger other)
    • BigInteger multiply(BigInteger other)
    • BigInteger divide(BigInteger other)
    • BigInteger mod(BigInteger other)
    • int compareTo(BigInteger other)
    • static BigInteger valueOf(long x)
  • java.math.BigDecimal
    • BigDecimal add(BigDecimal other)
    • BigDecimal subtract(BigDecimalother)
    • BigDecimal multiply(BigDecimalother)
    • BigDecimal divide(BigDecimal other, RoundingMode mode) // mode表示舍入方式,RoundingMode.HALF_UP表示四舍五入。
    • int compareTo(BigDecimal other)
    • static BigDecimal valueOf(double x)

3.10 数组

  • 数组是一种数据结构,用来存储同一类型值的集合。通过一个整型下标可以访问数组中的每一个值。
  • 声明数组:
    • int[] a = new int[n];
    • int a[] = new int[n];
    • 数组元素存储在heap中,因此具有默认初始化值。数值类型为0,char为\u0000,boolean为false,引用类型为null。
    • 一旦创建了数组,就不能再改变它的大小。
    • Arrays.toString(Object[] arr)将返回一个包含数组所有元素的字符串,格式为”[e0, e1,e2…]”。
    • 数组长度可以为0.

3.10.1 for each循环(增强for循环)

  • for (type variable : collection){ statement },意为”for each element in collection”。
  • collection可以是数组或任意实现了Iterable接口的 类对象。

3.10.2 数组初始化以及匿名数组

// 先声明一个数组,然后初始化每个元素。
int[] arr = new int[5];
arr[0] = 1;
arr[1] = 2;
arr[2] = 3;
arr[3] = 4;
arr[4] = 5;
// 声明数组的同时初始化。
int[] arr = new int[]{1,2,3,4,5};
// 简写。
int[] arr = {1,2,3,4,5};
// 创建一个匿名数组。
new int[]{1,2,3,4}; // 注意,不能单独创建一个匿名数组而没有其他任何操作(如传值、打印等),编译会报错。
// 用一个匿名数组重新初始化一个数组变量。
arr = new int[]{1,2,3,4};
// 简写。
arr = {1,2,3,4};
// 以下的写法是错误的,不能同时声明数组的长度和具体元素。
int[] arr = new int[5]{1,2,3,4,5};

3.10.3 数组拷贝

  • Java中允许将一个引用变量拷贝给另一个引用变量,此时,两个变量引用同一个对象。
int[] a = new int[5];
int[] b = a;
b[0] = 1;
System.out.println("a[0]=" + a[0]); // a[0]=1
  • 可以使用Arrays.copyOf(Object[] obj, int newLength)方法将一个数组中的全部或部分元素拷贝到一个新的数组中。
int[] a = new int[]{1,2,3,4,5};
int[] b = Arrays.copyOf(a, 3); // 只拷贝前3个元素,超出新数组长度部分被舍弃。
System.out.println(Arrays.toString(b)); // [1, 2, 3]
int[] c = Arrays.copyOf(a, 7); // 拷贝全部元素,新数组不足部分用默认值补足。
System.out.println(Arrays.toString(c)); [1, 2, 3, 4, 5, 0, 0]

3.10.4 命令行参数

  • Java中的main方法接收一个字符串数组,这个数组可以通过命令行参数传入。
// java Demo -1 2 3
public class Demo {
    public static void main(String[] args) {
        System.out.println(Arrays.toString(args)); // [1,2,3]
    }
}

3.10.5 数组排序

  • java.util.Arrays
    • static String toString(type[] a)
    • static type[] copyOf(type[] a, int newLength)
    • static type[] copyOfRange(type[] a, int start, int end)
    • static void sort(type[] a)
    • static int binarySearch(type[] a, type v)
    • static int binarySearch(type[] a, int start, int end, type v)
    • static void fill(type[] a, type v)
    • static boolean equals(type[] a, type[] b)

3.10.6 多维数组

  • 多维数组将使用多个下标访问数组元素,它适用于表示表格或更加复杂的排列形式。二维数组也称为矩阵。
  • Arrays.deepToString(Object[] arr)将返回包含一维数组和二维数组全部元素的字符串。

3.10.7 不规则数组

  • Java实际上没有多维数组,只有一维数组。多维数组被解释为“数组的数组”,即元素是另一个数组的数组。
  • 数组的每一行(即每个元素数组)长度不相等的二维数组,称为不规则数组。
int[][] odds = new int[10][];
odds[0] = new int[1];
odds[1] = new int[2];
// ...
posted @ 2017-07-13 20:32  李崎荷  阅读(191)  评论(0编辑  收藏  举报