Java核心技术阅读笔记(二)Java的基本程序设计

Java的基本程序设计

目录

  1. Java应用程序的基本组成
  2. Java数据类型
  3. 变量与常量

1.Java应用程序的基本组成

/**
 @author...
 **/
package com.company;
public class Main {//行注释
    public static void main(String[] args) {
     System.out.println("hello");
    }
}
/*
段注释
段注释不能嵌套
*/
  1. 第一部分声明package的名称,这个可以作为后面将Java程序打包使用
  2. public class Main每个Java文件有且仅有一个public class,Java严格区分大小写,包含public class的文件要以这个公共类的名称保持一致,且扩展名为.java
  3. 程序执行的时候从默认从public class中的public static void main方法入口进入,也可以指定由哪个类的main方法进入,⚠️Java是强类型的,所以方法是依附于类存在的,所以main方法也有一个shell。如果main方法正常退出,Java应用程序的退出码是0,表示成功运行了程序,如果想要指定退出码可以使用system.exit进行指定。
  4. System.out.println输出换行System.out.print输出不换行
  5. 注释

2.Java数据类型

Java是一种强类型语言,必须为每个变量声明一种类型。

primitive type 存储要求
int 4字节,最常用
short 2字节,主要用于特定场景,比如底层文件处理或者存储空间很宝贵时的大数组
long 8字节,主要用于存储比如,全球人口,这种超大数,但是更大的数实际上会使用"big number"包中的相关类
byte 1字节,主要用于特定场景,比如底层文件处理或者存储空间很宝贵时的大数组
float 4字节,只有很少的情况下使用float类型,比如需要单精度的库,或者需要存储大量的数据
double 8字节
char 本来用于表示单个字符,现在也可以用于表示Unicode字符
boolean false/true,用来判断逻辑条件,整型值和布尔值之间不能互换

Java中所有的数值类型所占据的字节数与平台无关。
Java没有任何无符号(unsigned)形式的int、long、short或byte类型

2.1 整型
  1. 表示方法
long:后缀L/l
十六进制:前缀0x/0X
八进制:前缀0(不要使用8进制常数)
二进制:0b/0B
数字字面量可以加下划线:1_000_000(Java编译器会删去下划线)
  1. 有符号数和无符号数
    没有任何无符号数,但是可以把有符号数解释成无符号数。但是要注意取值范围,只要不溢出加减乘都可以运算,但是对于其他运算,需要调用Byte.toUnsignedInt(b)来得到一个0~255的int值,然后处理这个整数值,再把它转回byte,Integer类和Long类都有无符号除法和求余数的方法。(以byte为例)
2.2 浮点型
  1. 表示方法
16进制表示法:0.125=0x1.0p-3
p表示指数而不是e,而且尾数采用16进制,指数采用10进制。指数的基数是2,不是10.
  1. 3个特殊的浮点数值
Double/Float.POSITIVE INFINITY:正无穷大(正数处以0)
Double/Float.NEGATIVE INFINITY:负无穷大
Double/Float.NaN(0/0或负数平方根)不是一个数字

所有非数值的值都认为是不相同的,不能这样检测
int x = 1;
if (x==Double.NaN)
    System.out.println();
可以使用Double.isNaN(x)进行判断,无法进行直接判断的去找找有没有相应的函数
2.3 char类型和Unicode
  1. 表示方法
char 类型要用单引号扩起来
'A'
char 类型可以表示为16进制值:\u0000~\uFFFF

转义字符:
所有转义字符都可以出现在加引号的字符字面量或字符串中,转义序列\u还可以出现在之外,Unicode转义序列会在解析代码前得到处理。

⚠️注释中的\u:
//\u000A is a newline
这里\u是会被解析出来的,直接会被换行,后面的出现在下一行中出现错误
// look inside: c:\users
这里也会产生语法错误,\u后面没有跟16进制数
  1. unicode编码

码点:与一个编码表中的某个字符值对应的代码值
基本多语言平面:Unicode的编码可以分为17个代码平面,第一个代码平面是基本多语言平面(U+0000~U+FFFF)
其余的16个从U+100000~U+10FFFF,包括辅助字符。
代码单元:基本多语言平面中,每个字符用16位表示,称为一个代码单元,辅助字符占用两个连续的代码单元。可以迅速的知道一个代码单元是一个字符的编码还是一个辅助字符的第一部分或第二部分。
3. 最好将字符串作为抽象数据类型处理,而不使用char

3. 变量与常量

3.1 变量
  1. 变量声明typename varietyname;
    变量命名的规则和其他语言一致(这里$是一个合法字符,但不要在自己的代码中使用,这个字符只用在Java编译器或其他工具生成的名字
  2. 变量初始化
    变量使用之前必须初始化,而且变量的声明要尽可能的靠近变量第一次被使用的地方。
    从Java 10 开始,局部变量可以从变量的初始值推测出它的类型,不再需要声明类型。
var vacationDays = 12;//vacationDays is an int 
3.2 常量
final double CM_PER_INCH = 2.54;
//final表示这个变量只能被赋值一次,一旦赋值之后不可更改,常量名全部大写

public class Constant{
 public static final double  CM_PER_INCH= 2.54;
 //这属于在类中声明类常量,这样可以在一个类的多个方法中使用类常量
 //声明为public其他类也可以使用这个常量,但是从逻辑上说,这个常量是属于这个类的,所以在类里面设置,也符合Java强类型
  public static void maint(String[] args)
   {
        //write code here...
    }
 }

3.3 枚举类型
enum Size{small,medium,large};
Size s = Size.small;
声明和使用如上所示,Size类型的变量只能存储这个类型声明中给定的某个枚举值,或者特殊值null,null表示这个变量没有设置任何值。

4. 运算符

4.1 算术运算符

正常的加减乘除;在使用/参与除法运算时,如果是两个整数表示整数出发,否则表示浮点数除法

在Intel处理器中如果进行64位计算的话,比如answer = xy/z,会先计算xy然后将中间结果存储在80位的寄存器中国,然后再进行除法运算,最后将结果截断成64位。注意截断操作不仅损失精度而且截断操作会降低处理速度。

public static strictfp void main(String[] args)
{
//使用strictfp标志这个方法中的所有计算都使用严格的浮点计算模式,即中间过程也使用64位寄存器
}

两种方式的区别仅在于默认方式不会产生溢出,而严格的计算有可能产生溢出。

4.2 数学函数与常量
常用三角函数:
Math.sin
Math.cos
Math.tan
Math.atan
指数函数及反函数
Math.exp
Math.log
Math.log10
表示e和pai常量最接近的近似值:
Math.PI
Math.E

Math中的函数只返回一个错误结果而不作提醒。

4.3 数值类型之间的转换
  1. 当用一个二元运算符连接两个值是,先将二者的类型统一再计算
  2. 如果想要对浮点数进行舍入运算,以便得到最接近的整数,需要使用Math.round方法:
double x = 9.99;
int nx = (int)Math.round(x);

这里注意还是要有一个int的强制类型转换,因为Math.round的返回类型是long

  1. boolean转换成整数
int answer = b ? 1 : 0;
4.4 结合赋值和运算
x+=4;
//如果运算符得到一个值,其类型和左侧操作数的类型不同,就会发生强制类型转换。
n++;
n--;
//自增自减
4.5 关系运算和布尔操作符
&&||运算是按照“短路”方式求值的,如果第一个操作数已经能够确定表达式的值,第二个操作数就不必计算了。
可以利用这一点避免错误。
4.6 位运算符

使用掩码技术得到整数中的各个位。

^(xor) | & ~(not)
//不采用短路方式,每一项都执行
>> << 用符号位填充高位
//逻辑移位
>>>0填充高位
移位运算符要完成对右操作数模32的操作,比如1<<35等价于1<<38
如果是long类型,对右操作数模64
取出一个数中的某一位:
int number = (a&(1<<n))>>n;
n为具体要取的是第几位,如:
int number = (a&(1<<3))>>3;//取出第三位

5.字符串

5.1 子串
String greeting = "Hello";
String s = greeting.substring(0,3);
//从0开始计数,第一个数是起始位,第二个数是子串长度
5.2 拼接
String str1 = "hello";
String str2 = "world";
String answer = str1 + str2;

int num = 3;
String  str = "string";
String answer = str+num;
//string3
1. 多个字符串拼接在一起,使用一个规定的分隔符
String str = String.join("/","a","b","c","d");
// 结果:a/b/c/d
2. repeat方法
String str="java".repeat(3)
// 结果:javajavajava
5.3 不可变字符串

String类没有提供修改字符串中某个字符的方法。

可以提取想要保留的子串,然后与希望替换的字符拼接
String newstring = str.substring(0,3)+"p!";

由于不能修改Java字符串中的单个字,所以在Java文档中把String类对象称为不可变的。字符串本身是不可改变的,但是字符串变量是可以修改它的指向的。各种字符串存放在公共的存储池中,字符串指向存储池中相应的位置,如果复制一个字符串变量,原始字符串与复制的字符串共享相同的字符。
Java的设计者认为:共享带来的高效率远远胜过提取子串、拼接字符串所带来的低效率。

5.5 检测字符串是否相等
//检测两个字符串是否相等,返回boolean值
//s和t可以是字符串变量也可以是字符串常量
s.equals(t);
"hello".equals(s);
//也可以使用类C++
if(greeting.compareTo("hello")==0)
   //do something
  
s == t;
//判断的是这两个字符串是否存放在同一个位置
//因为完全可以将内容相同的两个字符串放在不同的位置

如果虚拟机始终将相同的字符串共享,就可以使用==检测是否相等,但是实际上只有字符串字是共享的,而+或substring等操作得到的字符串并不共享。

5.6 空串和Null串

空串""是指长度为0的字符串,而Null串就是string存储的一个特殊的值,==表示目前没有任何对象与该变量关联。

if(str.length()==0)
if(str.equals(""))
//空串是一个Java对象,有自己的串长度(0)和内容(空)
if(str == null)
//有时要检查一个字符串既不是null也不是空串,首先检查str不为null
if(str!=null&&str.length()!=0)
5.7 码点和代码单元
1. length方法
采用UTF-16编码方式,表示给定字符串的“代码单元”的数量
String str = "hello";
int n = str.length();

2. 得到实际长度,“码点数量”
int cpCount = str.codePointCount(0,str.length());

3. 得到第i个代码单元
String i = str.charAt(i);

4. 得到第i个码点
int index = str.offsetByCodePoints(0,i);
int cp = str.codePointAt(index);

5. 遍历字符串,依次查看每个码点
int cp = sentence.codePointAt(i);
if(Character.isSupplementaryCodePoint(cp))
   i+=2;//如果是特殊字符,不是基本字符每个占两个码点
else i++;

6. 反向遍历
i--;
if(Character.isSurrogate(sentence.charAt(i))) i--;
int cp = sentence.codePointAt(i);

7. 流的思想
int[] codePoints = str.codePoints().toArray();
//codePoints方法生成一个int值的字符流,每个int值对应一个码点,可以将其转换成一个int型的数组
String str = new String(codePoints,0,codePoints.length);
//由已知的码点数组生成一个字符串
5.8 String API

Charcharacter是一种接口类型,所有的字符串都属于这个接口,完全可以穿入String类型的实参。

1. char charAt(int index);
2. int codePointAt(int index);
3. int offsetByCodePoints(int startIndex,int cpCount);
4. int compareTo(String other);
//按照字典顺序,如果字符串位于other之前,返回一个负数;位于之后返回正数;相等返回0
5. IntStream codePoints();
6. new String(int[] codePoints,int offset,int count);

7. boolean empty();
8. boolean blank();//如果字符串为空或者由空格组成,返回true

9. boolean equals(Object other)10. boolean equalsIgnoreCase(String other);
11. boolean startsWith(String prefix);
12. boolean endsWith(String suffix);

13. int indexOf(String str);
14. int indexOf(String str,int fromIndex);
15. int indexOf(int cp);
16. int indexOf(int cp,int fromIndex);
//返回与字符串或码点匹配的第一个子串开始的位置,从索引0或fromIndex开始匹配,如果原始字符串中不存在str则返回-1

17. int lastIndexOf(String str);
18. int lastIndexOf(String str,int fromIndex);
19. int lastindexOf(int cp);
20. int lastindexOf(int cp,int formIndex);
//返回匹配的最后一个子串的位置

21. int length();
22. int codePointsCount(int startIndex,int endIndex)

23. String replace(CharSequence oldString,CharSequence newString);
//使用newString代替原始字符串中的所有oldString
//可以使用String/StringBuilder对象作为replace参数

24. String subString(int beginIndex);
25. String subString(int beginIndex,int endIndex);

26. String toLowerCase();
27. String toUpperCase();

28. String trim();
29. String strip();
//返回一个新的字符串,这个字符串将删除原始字符串头部和尾部小于U+0020的字符(trim)或空格(strip)

30. String join(CharSequence delimiter,CharSequence...elements);
//返回一个新字符串,用给定的定界符delimiter连接所有元素
31. String repeat(int count);
//当前字符重复count次
5.9 构建字符串

每次拼接字符串的时候都会构建一个新的String对象。使用StringBuilder来进行构建。

StringBuilder stringbuilder = new StringBuilder();
stringbuilder.append("hello");
stringbuilder.append("world");
...
//当所有的小字符串都拼接成功后,一起转换成String
String completedString = builder.toString();

StringBuffer:多线程
StringBuilder:单线程
二者的API完全相同

1. StringBuilder();
2. int length();

3. StringBuilder append(String str);
4. StringBuilder append(char ch);
5. StringBuilder appendCodePoint(int cp);
//追加内容
6. void setCharAt(int i,char c);
//将第i个代码单元改成c
7. StringBuilder insert(int offset,String str);
8. StringBuilder insert(int offset,char c);
//在offset插入一个字符串或字符
9. StringBuilder delete(int startIndex,int endIndex);
//删除startIndex到endIndex-1的代码单元并返回this 
10.String toString();  

6. 输入与输出

6.1 读取输入
Scanner scan = new Scanner(System.in);
String name = in.nextLine();
int age = in.nextInt();

常见API:

Scanner(InputStream in);
//读入内容
String nextLine();//读取输入的下一行内容
String next();//读取输入的下一个单词(以空格作为分隔符)
int nextInt();
double nextDouble();
//检测输入中是否还有其他内容
boolean hasNext();
boolean hasNextInt();
boolean hasNextDouble();
6.2 文件输入与输出

  • 文件读取:
    读取一个文本文件的时候,要知道它的字符编码,如果省略字符编码,则会使用运行这个Java程序的机器的“默认编码”。
Scanner scan = new Scanner(Path.of("myfile.txt",StandardCharsets.UTF_8);
//这里的第一个参数必须是文件的路径名,如果只是一个文件名的话,会出现错误,因为Scanner会把其解释成数据而不是文件

找到当前的目录

String dir = System.getProperty("user.dir");

  • 文件输入:
    如果文件不存在,创建该文件。可以像输出到System.out一样用print、println以及printf命令。
PrintWriter out = new PrintWriter("myfile.txt",StandardCharsets.UTF_8);

⚠️如果用一个不存在的文件构造一个Scanner,或者使用一个无法创建的文件名构造一个PrintWriter,就会产生异常,这个异常比除0⃣️异常更为严重,需要处理:

public static void main(String[] args) throws IOException
{
  Scanner scan = new Scanner(Path.of("myfile.txt",StandardCharsets.UTF_8);
  ...
}

使用API:

Scanner(Path p,String encoding);//构造一个使用给定字符编码从给定路径读取数据的Scanner
Scanner(String data);//构造一个从给定字符串读取数据的Scanner

PrintWriter(String filename);

static Path of(String pathname);
//根据给定的路径名构造一个Path

7. 流程控制

  1. case的标签可能是:
- 类型为charbyteshortint的常量表达式
- 枚举常量:不必指定枚举名比如Size.SMALL,直接使用case SMALL即可
- Java 7 开始case标签还可以是字符串字面量
  1. 中断控制流程语句
    带标签的break语句,如果由break退出,会跳转到带标签的语句块末尾。
read_data:
 while(1){
    for(int i =0;i<10;i++)
      {
        ...
        break read_data;
      }
 }

与任何使用break语句的代码一样,然后需要检测循环是正常结束还是由break跳出
标签如果没有自己跟{}就是下面自然定义的语句块,否则labelname{}

同样带标签的continue语句跳转到与标签匹配的循环的首部
  1. 大数
BigInteger 和 BigDecimal:实现任意精度的整数和浮点数计算
不能使用+-等运算符,使用专用add,mutiply等方法

常用API:

BigInteger/BigDecimal add(BigInteger/BigDecimal other);
BigInteger/BigDecimal substract(BigInteger/BigDecimal other);
BigInteger/BigDecimal multiply(BigInteger/BigDecimal other);
BigInteger/BigDecimal divide(BigInteger/BigDecimal other);
BigDecimal divide(BigDecimal other,RoundingMode mode);
//后面是舍入方式
BigInteger/BigDecimal mod(BigInteger/BigDecimal other);
int compareTo(BigInteger/BigDecimal other);
//返回比较结果,相等返回0,大返回正数,小返回负数
static BigInteger/BigDecimal valueOf(long x);
static BigDecimal valueOf(long x,int scale);//返回值等于x/100^scale的一个大实数
  1. 数组

  • 声明数组
int[]a = new int[10];
数组长度不要求是常量,可以是变量:
int n = 10;
int[] a = new int[n];

在Java中,允许有长度为0的数组,在编写一个结果为数组的方法时,如果碰巧结果为空,这样一个长度为0的数组就和有用。可以创建如下长度为0的数组:

new elementType[0];
new elementType[]{};//注意长度为0的数组与null并不相同

  • 访问数组元素
1. 创建一个数字数组时,所有元素都初始化为0
2. boolean数组的元素初始化为false
3. 对象数组的元素初始化为null

如果希望这个数组包含空串,必须指定空串:

for(int i=0;i<10;i++) names[i]="";

获得数组中元素的个数:

int length = array.length;

  • for each循环
for (variable:collection) statement
//collection这一集合表达式必须是一个数组或一个实现了Iterable接口的类对象

简单方式打印数组
Arrays.toString(a)返回一个包含数组元素的字符串,包含在括号内,使用逗号间隔


  • 数组拷贝
int[] copiedLuckyNumbers = Arrays.copyOf(LuckyNumbers,LuckyNumbers.length);
//第二个参数是新数组的长度,这个方法通常用来增加数组的大小

  • 命令行参数
public static void main(String[] args)
//其中的args就是接收命令行参数使用的,程序名没有存储在args数组中
java Message -g cruel world
args[0]:-g
args[1]:cruel
args[2]:world

  • 数组排序
    常用API:
static String toString(xxx[] a);//常见8大类型的数组都可以

static xxx[] copyOf(xxx[] a,int length);
static xxx[] copyOfRange(xxx[] a,int start ,int end);
//如果end的值大于length的值,结果会填充0或false

static void sort(xxx[] a);
static int binarySearch(xxx[] a,xxx v);//如果找到返回相应的下标,没有返回一个负数值
static int binarySearch(xxx[] a,int start,int end,xxx v);

static void fill(xxx[] a,xxx v);//将数组的所有数据元素设置为v
static boolean equals(xxx[] a,xxx[] b);//两个数组大小相同且对应下标的元素相等


  • 多维数组
for ( double[] row:a)
   for( double value:row)
      do something with value

多维数组要使用两个for-each循环来使用第一个的迭代元素是一个一维的数组,第二个才是一维数组里的单个数据


  • 不规则数组
int[][] odds = new int[MAX+1][];
for (int n=0;n<NMAX;n++)
   odds[n] = new int[n+1];

每一行又是新的长度的数组。

你可能感兴趣的:(Java核心技术卷I)