Java修炼入门 P1

Java是一种广泛使用的计算机编程语言,拥有跨平台面向对象泛型编程的特性,广泛应用于企业级Web应用开发和移动应用开发。

Java编程语言的风格十分接近C++语言。继承了C++语言面向对象技术的核心,舍弃了容易引起错误的指针,以引用取代;移除了C++中的运算符重载和多重继承特性,用接口取代;增加垃圾回收器功能。在Java SE 1.5版本中引入了泛型编程、类型安全的枚举、不定长参数和自动装/拆箱特性。

Java概述

Java特性

  • 简单性
    • Java基本的解释器以及类支持大约仅为40KB,再加上基础的标准类库和对线程的支持,大约需要增加175KB。
  • 面向对象
  • 分布式
    • Java例程库用来处理HTTP和FT之类的TCP/IP协议。
    • Java应用程序能够通过URL打开和访问网络上的对象。
  • 健壮性
    • Java采用的指针模型可以消除重写内存和损坏数据的可能性。
  • 安全性
  • 体系结构中立
    • 虚拟机可以将执行最频繁的字节码序列转换成机器码(即时编译)。
  • 可移植性
    • 数值类型有固定的字节数(如Java中的int永远为32位的整数)。
  • 解释型
  • 高性能
  • 多线程
    • 第一个支持并发程序设计的主流语言。
  • 动态性
    • 库中可以自由的添加新方法和实例变量。

Java程序设计环境

Java术语

Java Downloads

术语名 解释
Java SE(Standard Edition) 标准版(桌面 简单服务器应用)
Java ME(Micro Edition) 微型版(小型设备)
Java EE(Enterprise Edition) 企业版(复杂服务器应用)
JDK(Java Development Kit) Java开发工具包(编写Java程序软件)
JRE(Java Runtime Environment) Java运行时环境(运行Java程序软件,只包含虚拟机)
Server JRE 服务器JRE
JavaFX 图形化界面工具包
OpenJDK Java SE的开源实现
SDK(SoftWare Development Kit) 软件开发工具包
NetBeans Oracle公司的集成开发环境

命令行工具

1
2
3
4
#Java编译器
javac Hello.java
#Java程序启动Java虚拟机,虚拟机执行编译器编译到类文件的字节码
java Hello

1.注意Java源文件的大小写,类名为Welcome,源文件为Welcome.java。(源文件的文件名必须与公共类的名字相同,并用.java作为扩展名)

2.执行程序只需要指定类名,不要带扩展名(*.class)。

3.Java区分大小写。


Jshell

1
sudo apt install openjdk-16-jdk-headless #Ubuntu
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
stone@ubuntu:~/Desktop$ jshell
| Welcome to JShell -- Version 16.0.1
| For an introduction type: /help intro

jshell> "Core Java".length()
$1 ==> 9

jshell> 5 * $1 -3
$2 ==> 42

jshell> int answer = 6*7
answer ==> 42

jshell> Math.log
log( log10( log1p(
jshell> Math.log10(0.001)
$4 ==> -3.0

jshell> Math.log10(1000)

Tab补齐

Jdeprscan

Jdeprscan:检查代码中是否使用了Java API已经废弃使用的特性(as a static analysis tool that scans a jar file (or some other aggregation of class files) for uses of deprecated API elements)。

1
jdeprscan [ options ]{dir|jar|class}

基本程序设计结构

1
2
3
4
5
public class Hello {
public static void main(String[] args) {
System.out.println("Hello Java"); //使用System.out对象并调用了它的println方法
}
}

1.Java类名必须以字母开头,后面可以跟字母和数字的任意组合。

2.类名是以大写字母开头的名词,如果名字由多个单词组成,每个单词的第一个字母都应该大写(驼峰命名法,如FirstSample)。

3.运行已编译的程序时,Java虚拟机总是从指定类中的main方法(main方法必须声明为public)的代码开始执行。

4.在Java中,用大括号划分程序的各个部分(块)。Java中任何方法的代码都用”{“开始,用”}”结束。

5.在Java中每个句子必须用分号结束。

6.Java采用双引号界定字符串。


1.每个Java应用程序都必须有一个main方法。

2.Java中的方法可以没有参数,也可以有一个或多个参数。即使一个方法没有参数,也需要使用空括号。

3.Java中所有函数都是某个类的方法,因此Java中的main方法必须有一个外壳(shell)类。

4.Java中的main方法必须是静态的

5.关键词void表示这个方法没有返回值,main方法没有为操作系统返回”退出码“,如果main方法正常退出,那么Java应用程序的退出码为0,表示成功运行了程序。使用System.exit方法可以在终止程序时返回其他的退出码。

1
2
3
4
5
6
public class ClassName
{
public static void main(String[] args){
//programm statements
}
}
1
object.method(parameters)

print/printf/println

1.printf:可进行格式化输出。

2.print:标准输出(不在输出之后增加换行符)。

3.println:输出后换行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Hello {
public static void main(String[] args) {
final double PI = 3.1415;
System.out.println("Hello Java");
System.out.print("Hello Java");
System.out.printf("\n%f",PI);
System.out.printf("\n%.2f",PI);
}
}
/*
Hello Java
Hello Java
3.141500
3.14
*/

注释

1.//:其注释内容从//开始到本行结尾。

2./*annotation*/:将一段比较长的注释括起来。(不可嵌套

3,/**annotation*/:用来自动生成文档

1
2
3
4
5
/**
* This is the sample program in test
* @author Lucifer
* @version 1.00 2021-9-20
*/

数据类型

1.Java是一种强类型语言(必须为每一个变量声明一种类型)。

2.Java没有任何无符号(unsigned)形式的int、long、short或byte类型。

  • 如果要使用不可能为负的整数值而且确实需要额外的一位(bit),也可以把有符号整数值解释为无符号数。一个byte值b可以不表示范围-128到127,如果想表示0到255的范围,也可以存储在一个byte中。基于二进制算术运算的性质,只要不溢出,加法、减法和乘法都能正常运算。但对于其他运算,需要调用Byte.toUnsignedInt(b)来得到一个0到255的int值,然后处理这个整数值,再把它转换回byte。

  • Integer和long类都提供了处理无符号除法和求余数的方法。

1
2
3
4
5
6
byte b;
int uByte;
b = -1;
uByte = Byte.toUnsignedInt(b);
System.out.println(uByte); //255
System.out.println((byte)uByte); //-1

8种基本类型(primitive type):

  • 4种整型
    • int
    • short
    • long
    • byte
  • 2种浮点类型
    • float
    • double
  • 1种字符类型
    • char(表示Unicode编码的代码单元)
  • 1种boolean类型(表示真值)
    • boolean(true/false)
  • 整型:没有小数部分的数值,允许是负数。
Type Storage Requirement Range
int 4 bytes -2147483648~2147483647
short 2 bytes -32768~32767
long 8 bytes -9223372036854775808~9223372036854775807
byte 1 bytes -128~127

1.1 Byte = 8 bit 2^7 = 128 最高位表示正负,正数该位为0,负数该位为1。

2.整数的范围与运行Java代码的机器无关。

3.长整型long数值后有一个后缀L或l。

4.十六进制数值有一个前缀0x或0X。

5.八进制数值有一个前缀0。

6.二进制数值有一个前缀0b或0B。

7.可以在数字字面量加下划线,让人更易读,Java编译器会去除这些下划线。(Java7)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

public class TestType {
public static void main(String[] args) {
int num1 = 100;
short num2 = 121;
long num3 = 1000000000l;
long num4 = 10000000000L;
byte num5 = 122;
int num6 = 010;
int num7 = 0xCAFE;
byte num8 = 0b1001;
long num9 =120_0000_0000l;
//System.out.println(num1);
}
}
/*
100
121
1000000000
10000000000
122
8
51966
9
12000000000
*/

  • 浮点类型:表示有小数部分的数值。
Type Storage Requirement Range
float 4 bytes Approximately ±3.40282347E+38F (6–7 significant decimal digits)
double 8 bytes Approximately ±1.79769313486231570E+308 (15 significant decimal digits)

1.大部分情况使用double类型(双精度数值)。

2.float类型的数值有一个后缀F或f。没有后缀F的浮点数默认为double类型。

3.double类型的数值也可以添加后缀D或d。

1
2
3
float num1 = 3.1415f;
double num2 = 3.1415;
double num3 = 3.1415D;

可以使用十六进制表示浮点数。在十六进制表示法中,使用p表示指数。注意尾数采用十六进制,指数采用十进制。指数的基数是2。

1
double num=0x4.0p-3; //0.5 0.5 = 4.0*(2^-3)

1.所有的浮点数值计算都遵循IEEE 754规范,用于表示溢出和出错情况的三个特殊的浮点数值:

  • 正无穷大
  • 负无穷大
  • NaN

2.整数(包括0)除以0结果会抛出异常(java.lang.ArithmeticException)。0.0/0或者负数的平方根结果为NaN。浮点数(不包括0.0)/0结果为Infinity。

3.常量Double.POSITIVE_INFINITY、Double.NEGATIVE_INFINITY和Double.NaN(以及相应的Float类型的常量)分别表示这三个特殊的值。

4.所有非数值的值都认为是不相同的。使用Double.isNaN等方法判断。

5.浮点数值不适用于无法接受舍入误差的金融计算。如果数值计算中不允许有任何舍入误差,就应该使用BigDecimal类。

1
2
3
4
5
6
7
8
9
10
11
12
13
System.out.print(1.2/0); //Infinity
System.out.print(0.0/0); //NaN
System.out.print(Math.sqrt(-1.0)); //NaN


System.out.println(Double.POSITIVE_INFINITY); //Infinity
System.out.println(Double.NEGATIVE_INFINITY); //-Infinity
System.out.println(Double.NaN); //NaN

double x=0.0/0;
System.out.println(Double.isNaN(x)); //true

System.out.println(2.0-1.1); //0.8999999999999999 二进制系统中无法精确地表示分数1/10

  • char类型

1.char类型原本表示单个字符。现在有些Unicode字符可以用一个char值描述,另外一些Unicode字符则需要两个char值。

2.char类型的字面量值要用单引号括起来。'A'是编码值为65所对应的字符常量,"A"是包含一个字符A的字符串。

3.char类型的值可以表示为十六进制值,其范围从\u0000到\uffff。

4.所有转义序列都可以出现在加引号的字符字面量或字符串中。转义序列\u还可以出现在加引号的字符常量或字符串之外。

5.码点(code point)是指与一个编码表中的某个字符对应的代码值。

1
2
3
4
5
6
7
8
System.out.println('\u2122'); 
System.out.println('\u03c0');
System.out.print("\u0022+\u0022");
/*

π
(无输出""+""空串)
*/
Escape Sequence Name Unicode Value
\b Backspace \u0008
\t Tab \u0009
\n Linefeed \u000a
\r Carriage return \u000d
\\" Double quote \u0022
\' Single quote \u0027
\\ Backslash \u005c

1.Unicode转义序列会在解析代码之前得到处理。

2.一定要当心注释中的\u。

3.在Java中,char类型描述了UTF-16编码中的一个代码单元。

4.建议不要在程序中使用char类型,除非确实需要处理UTF-16代码单元。最好将字符串作为抽象数据类型处理。


  • boolean类型

1.boolean(布尔)类型有两个值:false和true,用来判定逻辑条件。

2.整型值和布尔值之间不能进行相互转换,避免了诸如if(x=0)的麻烦出现。(在C++中可以编译运行,其结果总是false。而在Java中不能通过编译,因为整数表达式x=0不能转换为布尔值。)


变量与常量

变量

在Java中,每个变量都有一个类型(type)。在声明变量时,变量的类型位于变量名之前。

1
2
3
4
5
6
/* type variablename; */
double salary;
int vacationDays;
int i, j;
Box box;
Box abox;

1.变量名必须是一个以字母开头并由字母或数字构成的序列。

2.字母包括'A'~'Z'、'a'~'z'、'_'、'$'(只用在Java编译器或其他工具生成的名字)或在某种语言中表示字母的任何Unicode字符(使用Character类的isJavaIdentifierStartisJavaIdentifierPart方法查看)。数字亦然,但'+'这样的符号不能出现在变量名中,空格也不行。

3.变量名大小写敏感0(当名字选择困难时,也可以将变量名命名为类型名(前提是不冲突)或者变量名前再加上前缀"a")。

4.变量名的长度基本没有限制。

5.不能使用Java保留字作为变量名。

6.可以在一行中声明多个变量(不提倡),逐一声明每一个变量可以提高程序的可读性。


1.声明一个变量之后,必须用赋值语句对变量进行显式初始化不要使用未初始化的变量

2.初始化

  • 将变量名放在等号(=)左侧,相应Java表达式放在等号的右侧。
  • 将变量的声明和初始化放在同一行中。

5.在Java中可以将声明放在代码中的任何地方。

6.在Java中,变量的声明尽可能地靠近变量第一次使用的地方。

7.在Java中,不区分变量的声明和定义。

1
2
3
4
int VacationDays;
vacationDays = 12;

double salary = 65000.0;

Java 10开始,对于局部变量,如果可以从变量的初始值推断出它的类型,就不需要声明类型。只需要使用关键字var而无须指定类型。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class HelloWorld{
public static void main(String[] args){
var vacationDays =12; //int
var greeting = "Hello"; //String
System.out.println(getType(vacationDays));
System.out.println(getType(greeting));
}

private static String getType(Object object) {
return object.getClass().getName();
}
}
/*
java.lang.Integer
java.lang.String
*/

常量

1.在Java中,利用关键字final指示常量,即这个变量只能被赋值一次。一旦被赋值之后,就不能够再更改了。

2.习惯上,常量名使用全大写

3.在Java中,如果希望某个常量可以在一个类中的多个方法中使用,通常将这些常量称为类常量(定义位于main方法的外部),可以使用关键字static final设置一个类常量。

4.在同一个类的其他方法中也可以使用类常量,而且如果一个常量被声明为public,那么其他类的方法也可以使用这个常量

1
2
3
4
5
6
7
8
9
10
11
package com.noobs.base;

public class Constans {
public static void main(String[] args) {
final double CM_PER_INCH = 2.54;
double paperWidth = 8.5;
double paperHight = 11;
System.out.println("Paper size in centimeters:"+paperWidth*CM_PER_INCH+" by "+paperHight*CM_PER_INCH);
System.out.print(ConstantTest.PI_NUM);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
package com.noobs.base;

public class ConstantTest {
public static final double PI_NUM = 3.1415;

public static void main(String[] args) {
System.out.println(PI_NUM);
test();
}

public static void test() {
System.out.printf("%.2f",PI_NUM);
}
}

枚举类型

当变量的取值只在一个有限的集合,可以自定义枚举类型(包括有限个命名的值)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package com.noobs.base;

public class Enumeration {
public static void main(String[] args) {
enum Size{SMALL, MEDIUM, LARGE, EXTRA_LARGE};
Size s = Size.MEDIUM;
Size t = Size.EXTRA_LARGE;
System.out.println(s); //MEDIUM
System.out.println(t); //EXTRA_LARGE
}
}
/*
Size类型的变量只能存储这个类型声明中给定的某个枚举值,或者特殊值null,null表示这个变量没有设置任何值。
*/

操作符

算术运算符

1.当参与/运算的两个操作数都是整数时,表示整数除法;否则,表示浮点除法。
2.整数被0除将会产生一个异常,而浮点数被0除将会得到无穷大或NaN结果。

3.使用strictfp关键字标记的方法必须使用严格的浮点计算来生成可再生的结果。

1
2
System.out.println(2.0+1); //3.0
System.out.println(5%2); //1

数学函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package com.noobs.base;

public class Opration {
public static void main(String args[]) {
double x=4;
//平方根
double y=Math.sqrt(x);
//幂运算
double z=Math.pow(x, y);
//floorMod floorMod(position+adjustment,12)总会得到一个0~11之间的数。但对于负除数,floorMod会得到负数结果。
double w=Math.floorMod(13+4,11);
System.out.println(y);
System.out.println(z);
System.out.println(w);
}

}
/*
2.0
16.0
6.0
*/

println方法和sqrt方法存在差异:println方法处理System.out对象,但Math类中的sqrt方法处理的不是对象(静态方法)。

1
2
3
4
5
6
7
8
9
10
11
12
13
package com.noob.base;
import static java.lang.Math.*;
/*
* 静态导入
*/

public class Opration {

public static void main(String args[]) {
double y=sqrt(PI);
System.out.println(y);
}
}

Math类

1.常用三角函数

  • Math.sin
  • Math.cos
  • Math.tan
  • Math.atan
  • Math.atan2

2.指数函数以及反函数

  • Math.exp
  • Math.log
  • Math.log10

3.常量

  • Math.PI
  • Math.E
1
2
3
4
5
6
7
8
9
10
11
12
13
package com.noob.base;

public class Opration {

public static void main(String args[]) {
double y=Math.sin(12);
double z=Math.atan2(0.2,0.3);
double w=Math.log10(100);
System.out.println(y);
System.out.println(z);
System.out.println(w);
}
}

1.如果得到一个完全可预测的结果比运行速度更重要,就应使用StrictMath类,确保在所有平台上得到相同的结果。

2.如果一个计算溢出,数学运算符通常会返回错误的的结果而不做任何提醒。

3.Math.multiplyExact对于计算溢出的结果可以生成一个异常,可以捕获这个异常或者让程序终止。类似可以处理int和long参数的还有:

  • Math.addExact
  • Math.subtractExact
  • Math.incrementExact
  • Math.decrementExact
  • Math.negateExact
1
2
3
4
5
6
7
8
9
10
package com.noob.base;

public class Opration {

public static void main(String args[]) {
System.out.println(1000000000*3); //-1294967296
System.out.println(Math.multiplyExact(1000000000,3)); //Exception in thread "main" java.lang.ArithmeticException: integer overflow

}
}

数值类型转换

实心箭头,表示无信息丢失的转换;虚箭头,表示可能有精度损失的转换。

1
2
3
4
5
6
7
8
9
10
package com.noob.base;

public class Opration {

public static void main(String args[]) {
int n=123456789;
float y=n;
System.out.println(y); //123456789是一个大整数,所包含的位数比float类型所能够表达的位数多,转换后得到同样大小的结果,但却失去了一定的精度。
}
}

1.当使用数值进行二元操作时,先将两个操作数转换为同一种类型,然后再进行计算

  • 如果两个操作数中有一个是double类型,另一个操作数就会转换为double类型。
  • 否则,如果其中一个操作数是float类型,另一个操作数将会转换为float类型。
  • 否则,如果其中一个操作数是long类型,另一个操作数将会转换为long类型。
  • 否则,两个操作数都将被转换为int类型。
1
2
3
double x=13.0;
int y=12;
System.out.println(y+x); //25.0

强制类型转换

1.必要时int类型的值将会自动地转换为double类型(位数低类型向位数高类型转换 byte,short,char-> int -> long -> float -> double)。但有时也需要将double转换成int。在Java中,允许进行这种数值之间的类型转换(有可能会丢失一些信息),即通过强制类型转换(cast)实现。

2.强制类型转换:(目标类型)变量名。

3.强制类型转换通过截断小数部分浮点值转换为整型

4.想对浮点数进行舍入运算,需要使用Math.round方法(调用round仍需使用强制类型转换(int),因为round方法返回结果为long类型,存在信息丢失的可能性,所以使用显式的强制类型转换)。

5.如果试图将一个数值从一种类型强制转换为另一种类型,而又超出了目标类型的表示范围,结果就会截断成一个完全不同的值。

6.不要在boolean类型与任何数值类型之间进行强制类型转换,防止发生错误。极少数的情况需要将布尔类型转换为数值类型可以使用条件表达式b?1:0

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.noob.base;

public class Opration {

public static void main(String args[]) {
double x=13.786;
int y = (int)x;
int z=(int)Math.round(x);
byte q=(byte) 300;
boolean r=true;
System.out.println(y); //13
System.out.println(z); //14
System.out.println(q); //44 超过范围返回错误值
System.out.println(r?1:0); //1
}
}

结合赋值和运算符

赋值中使用二元运算符

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package com.noob.base;

public class Opration {

public static void main(String args[]) {
int x=0;
x=x+4;
System.out.println(x);
int y=0;
y+=4; //运算符放在=号左边,如*=或%=
System.out.println(y);
x+=3.5; //x=(int)x+3.5; 如果运算符得到一个值,其类型与左侧操作数的类型不同,就会发生强制类型转换
System.out.println(x);


}
}

自增与自减运算符

后缀:n++ n–

前缀:++n –n

1.后缀和前缀形式都可使变量值加1或减1,但用在表达式中时前缀形式会先完成加减1,而后缀形式会使用变量原来的值

2.建议不要在表达式中使用++。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.noob.base;

public class Opration {

public static void main(String args[]) {
int m=7;
int n=7;
int a=2*++m;
System.out.println(a); //16
System.out.println(m); //8
int b=2*n--;
System.out.println(b); //14
System.out.println(n); //6

}
}

关系和布尔运算符

1.关系运算符

== != < > <= >=
等于 不相等 小于 大于 小于等于 大于等于

2.布尔运算符

& ||

1.短路(&&和||):如果第一个操作数已经能确定表达式的值,第二个操作数就不再进行计算。

2.Java支持三元操作符?:(condition ? expression1 : expression2),如果条件为true,就为第一个表达式的值,否则计算为第二个表达式的值。

1
2
3
4
5
6
x != 0 && 1 / x > x + y 
/*
&&运算符:当第一个表达式为false,那么结果就不可能为true。所以第二个表达式就不必计算了。
||运算符:当第一个表达式为true,值为true,无需计算第二个表达式。
利用这一点可避免错误,如该段代码避免了除数等于0的情况
*/
1
2
3
4
5
6
7
8
9
10
11
12
package com.noob.base;

public class Opration {

public static void main(String args[]) {
int x=1;
int y=2;
int z=x<y?x:y; //返回较小者
System.out.println(z);
}

}

位运算符

& | ^ ~ << >> >>>
and or xor not 左移(低位补0) 右移(高位正数补0,负数补1/符号位) 无符号右移(高位补0)
1
2
3
4
5
6
7
8
9
public class Opration {

public static void main(String args[]) {
int x=8;
int fourthBitFromRight=(x&0b1000);
System.out.println(Integer.toBinaryString(fourthBitFromRight)); //1000

}
}

1.&和|运算符不采用”短路”方式求值(计算结果之前两个操作数都需要计算)。

2.移位运算符的右操作数要完成模32的运算(除非左操作数是long类型,在这种情况下需要对右操作数模64)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package com.noob.base;

public class Opration {

public static void main(String args[]) {
int x=2;
int y=-8;
System.out.println(x<<1);
System.out.println(x>>1);
System.out.println(y>>1);
System.out.println(y>>>1);
}

}
/*
4
1
-4
2147483644
*/

括号与运算符等级

Operators Associativity
[] . () (method call) Left to right
! ~ ++ – + (unary) - (unary) () (cast) new Right to left
* / % Left to right
+ - Left to right
<< >> >>> Left to right
< <= > >= instanceof Left to right
== != Left to right
& Left to right
^ Left to right
| Left to right
&& Left to right
|| Left to right
?: Right to left
= += -= *= /= %= &= |= ^= <<= >>= >>>= Right to left

1.同一个级别的运算符按照从左到右的次序进行计算(右结合运算符除外)。

1
2
3
4
5
6
/*
&&的优先级比||的优先级高
a&&b||c 等价于 (a&&b)||c
+=是右结合运算符
a+=b+=c等价于a+=(b+=c)
*/

字符串

1.Java字符串就是Unicode字符序列。

2.Java没有内置的字符串类型,而是在标准Java类库中提供了一个预定义类,即String类。用双引号括起来的字符串都是String类的实例。

1
2
3
4
5
6
7
8
9
10
11
12
13
public class Opration {

public static void main(String args[]) {
String s1=""; //empty string
String s2="Hello World";
System.out.println(s1);
System.out.println(s2);
}
}
/*

Hello World
*/

子串

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/*
String类的substring方法可以从一个较大的字符串提取出一个子串
*/
public class LearnString {
public static void main(String args[]) {
String s="Hello Java";
System.out.println(s.substring(5));
System.out.println(s.substring(6,10));

}
}
/*
Java
Java
*/

1.Java字符串中的代码单元和码点从0开始计数。

2.substring方法易计算子串的长度,如字符串s.substring(a,b)的长度为b-a。


拼接

1.Java语言允许使用+号连接(拼接)两个字符串。

2.当将一个字符串与一个非字符串的值进行拼接时,后者被转换成字符串(任何一个Java对象都可以转换成字符串)。

3.String join(CharSequence delimiter, CharSequence… elements)

returns a new string joining all elements with the given delimiter. (static methodd)

4.String repeat(int count)

returns a string that repeats this string count times.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

public class StringLearn {

public static void main(String args[]) {
String s1="Hello";
String s2="World";
String s3=s1+s2;
int n1=10;
String s4=s1+n1+s2;
String s5=String.join("/", "S","M","L","XL");
String s6=s1.repeat(3);
System.out.println(s3);
System.out.println(s4);
System.out.println(s5);
System.out.println(s6);

}
}
/*
HelloWorld
Hello10World
S/M/L/XL
HelloHelloHello
*/

不可变字符串

1.String类没有提供用于修改字符串的方法,但可以修改字符串变量,让其引用另外一个字符串。

2.由于不能修改Java字符串中的字符,所以在Java文档中将String类对象称为不可变的(immutable)。编译器可以让字符串共享。Java字符串大致类似于char*指针。

1
2
3
4
5
6
public class LearnString {
public static void main(String args[]) {
String s="Hello Java";
System.out.println(s.substring(0,5)+"World"); //HelloWorld
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package com.noob.base;

public class StringStudy {
public static void main(String[] args) {
String s1="Java";
String s2="Java";
String s3="Ja"+"va";
System.out.println(s1==s2&&s2==s3); //true
/*
在z常量池中查找是否有"Java"对象,有则返回对应的引用实例,没有则创建对应的实例对象
即s1 s2 s3 都是引用的同一个对象
*/

String s4="Hello";
String s5=new String("Hello");
System.out.println(s4==s5); //false
System.out.println(s4.equals(s5)); //true
}
}

字符串相等

1.使用equals方法检测两个字符串是否相等。

1
s.equals(t)
  • 字符串s与t相等返回true,否则返回false。
  • s与t可以是字符串变量,也可以是字符串字面量。

2.使用equalsIgnoreCase方法检测两个字符串是否相等,而不区分大小写。

3.一定不要使用==运算符检测两个字符串是否相等,因为该运算符只能够确定两个字符串是否放置在同一个位置上。(实际上只有字符串常量是共享的,而+或substring等操作产生的结果并不是共享的)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class StringOperation {

public static void main(String[] args){
String s1="HelloWorld";
String s2="HelloWorld";
System.out.println(s1.equals(s2)); //true
System.out.println(s1.compareTo(s2)); //0 类似于c中的strcmp
System.out.println(s1.equalsIgnoreCase("helloworld")); //true

/*
System.out.println(s1.substring(0,3)=="Hel"); //Probably false
*/
}
}

空串与Null串

1.空串""是长度为0的字符串。

2.空串是一个Java对象,有自己的串长度(0)和内容(空)。

3.String变量可以存放一个特殊的值,名为null,这表示目前没有任何对象与该变量关联。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class StringOperation {

public static void main(String[] args){
String s1="";
String s2=null;
System.out.println(s1.length()); //0
System.out.println(s1.equals("")); //true
System.out.println(s2==null); //true
/*利用短路原理检测字符串不为null或者空串*/
System.out.println(s2!=null&&s2.length()==0); //false
/* NullPointerException:System.out.println(s2.length()); */

}
}

码点与代码单元

1.Java字符串由char值序列组成。char数据类型是一个采用UTF-16编码表示Unicode码点的代码单元。

2.length方法返回采用UTF-16编码表示的给定字符串所需要的代码单元数量。

3.使用codePointCount方法获取实际的长度即码点数量。

4.使用charAt(n)将返回位置n的代码单元,n介于0~s1.length()-1之间。

5.虚拟机不一定把字符串实现为代码单元序列。在Java9中,只包含单字节代码单元的字符串使用byte数组实现,所有其他字符串使用char数组。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
package com.noob.base;

public class StringOperation {

public static void main(String[] args){
String s1="HelloWorld";
System.out.println(s1.codePointCount(0,s1.length())); //10
System.out.println(s1.charAt(0)); //H
System.out.println(s1.charAt(5)); //W

/*得到第i个码点*/
int i=3;
int index =s1.offsetByCodePoints(0, i);
System.out.println(index); //3
int cp=s1.codePointAt(index);
System.out.println(cp); //108

String s2= "🍺 cheers";
System.out.println(s2.charAt(1)); //返回的不是一个空格,而是🍺的第二个代码单元,表示🍺需要两个代码单元

}

/*遍历一个字符串,并且依次查看每一个码点*/
void listCodePoint(String string){
int codePointNum =string.codePointCount(0,string.length() - 1);
int i = 0;
while (i <= codePointNum) {
int index = string.offsetByCodePoints(0,i);
int cp = string.codePointAt(index);

if (Character.isSupplementaryCodePoint(cp))
i += 2;
else
i++;
}
}

/*反向遍历
i--;
if(Character.isSurrogate(string.charAt(i))) i--;
int cp =string.codePointAt(i);
*/

/*
使用codePoints方法,它会生成一个int值的“流”,每个int值对应一个码点。可将其转换为一个数组,再完成遍历
int[] codePoints=str.codePoints().toArray();
将一个码点数组转换为一个字符串,可使用构造器
String str = new String(codePoints,0,codePoints.length);
*/
}

String API 文档

String API中文参考文档

String API参考文档

Java API


构建字符串

1.如果每次连接字符串都构建一个新的String对象,耗时又浪费空间,当需要用许多小段的字符串构建一个字符串使用时可以使用StringBuilder类(Java5 该类的前身是StringBuffer,其效率稍有些低,但允许采用多线程的方式执行添加或删除字符的操作。如果所有字符串在一个单线程中执行,则应该用StringBuilder)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class StringOperation {

public static void main(String[] args){
String s="World";
/*构建一个空的字符串构建器*/
StringBuilder builder = new StringBuilder();
/*当需要添加一部分内容时,调用append方法*/
builder.append("Hello");
builder.append(s);
/*需要构建字符串时就调用toString方法,将可以得到一个String对象,其中包含了构建器中的字符序列*/
String s1=builder.toString();
System.out.println(s1); //HelloWorld

}
}

输入与输出

读取输入

1.首先构造一个与标准输入流System.in关联的Scanner对象。

2.Scanner类读取输入方法:

  • nextline:读取一行的输入。
  • next:读取一个字符串(以空白符作为分隔符)。
  • nextInt:读取一个整数。
  • nextDouble:读取一个浮点数。
  • hasNext:检测输入中是否还有其他单词。(boolean)

3.Scanner类定义在java.util包中。当使用的类不是定义在基本java.lang包中时,一定要使用import指令导入对应的包。

1
2
import java.util.*;
import java.util.Scanner;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import java.util.*;
public class StringOperation {

public static void main(String[] args){
Scanner input = new Scanner(System.in);

String last_name = input.next();
String describe = input.nextLine();
int age = input.nextInt();
double weight = input.nextDouble();

System.out.println(last_name+":"+age+"year "+ weight+"kg "+describe);
}
/*
Piki Test is test
12
12.34
Piki:12year 12.34kg Test is test

test test is easy
14 13.8
test:14year 13.8kg test is easy
*/

}

next():

1.读取到有效字符后才可以结束输入。

2.对输入有效字符之前遇到的空白,next()方法会自动将其去掉。

3.只有输入有效字符后才将其后输入的空白作为分隔符或者结束符。

4.next()不能得到带有空格的字符串。

nextLine():

1.以Enter为结束符,也就是说nextLine()方法返回的是输入回车之前的所有字符。

2.可以获得空白。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import java.util.*;
public class StringOperation {

public static void main(String[] args){

Scanner input = new Scanner(System.in);
//判断输入的是否是整型 类似的还有hasNextDouble()
if(input.hasNextInt()) {
//接收整型
int i =input.nextInt();
System.out.println(i);
}else {
System.out.println("输入数据并非整型");
}

if(input.hasNext()) {
String i =input.next();
System.out.println(i);
}else {
System.out.println("输入数据并非字符串");
}
/*关闭输入流,节约资源*/
input.close();
}
}

Scanner类的输入是可见的,不适用于从控制台读取密码。可以使用Console(Java6)类。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import java.io.Console;
public class PasswordInput {
public static void main(String[] args) {
Console cons = System.console();
/*如果没有可用的控制台设备,则调用该方法将返回null */
if(cons!=null) {
String username = cons.readLine("User name:");
char[] passwd = cons.readPassword("Password:");
/*安全起见,返回的密码存放在一维字符数组中,而不是字符串中。对密码进行处之后,应该用一个填充值覆盖数组元素*/
}
}
}
/*
java.io.Console在控制台(cmd)中使用,在 Eclipse等IDE控制台无法使用。
*/

格式化输出

1
2
3
4
5
6
7
8
9
10
11
12
13
package com.noob.base;

public class ouput {
public static void main(String[] args) {
double x = 10000.0/3.0;
System.out.println(x); //将以x的类型所允许的最大非0数位个数打印输出x
System.out.printf("%8.2f",x); /*以一个字符宽度field width打印x:包括8个字符,另外精度为小数点后2个字符*/
/*
3333.3333333333335
3333.33
*/
}
}

1.Conversions for printf

1.printf方法中每一个以%字符开始的格式说明符都用相应的参数替换。

2.使用s转换符格式化任意的对象。对于实现了Formattable接口的任意对象都将调用formatTo方法;否则将调用toString方法,它可以将对象转换为字符串。

  • 使用静态的String.format方法创建一个格式化的字符串,而不打印输出。
1
String message = String.format("Hello,%s,Next year,you'll be %d", name,age);
Conversion Character Type Example
d Decimal integer 159
x Hexadecimal integer 9f
o Octal integer 237
f Fixed-point floating-point 15.9
e Exponential floating-point 1.59e+01
g General floating-point (the shorter of e and f)
a Hexadecimal floating-point 0x1.fccdp3
s String Hello
c Character H
b boolean true
h Hash code 42628b2
tx or Tx Date and time (T forces uppercase) Obsolete, use the java.time classes instead
% The percent symbol %
n The platform-dependent line separator

2.Flags for printf

printf方法可以使用指定控制格式化输出外观的各种标志。

Flag Purpose Example
+ Prints sign for positive and negative +3333.33 numbers. +3333.33
space(空格) Adds a space before positive numbers. | 3333.33|
0 Adds leading zeroes. 003333.33
- Left-justifies field. |3333.33 |
( Encloses negative numbers in parentheses. (3333.33)
, Adds group separators. 3,333.33
# (for f format) Always includes a decimal point. 3,333.
# (for x or o format) Adds 0x or 0 prefix. 0xcafe
$ Specifies the index of the argument to be formatted; for example, %1$d %1$x prints the first argument in decimal and hexadecimal. 159 9F
< Formats the same value as the previous specification; for example, %d %<x prints the same number in decimal and hexadecimal. 159 9F

3.Date and Time Conversion Characters

1.日期和时间的格式化包括两个字母,以t开始,以表中的任意字母结束。

1
2
import java.util.Date;
System.out.printf("%tc",new Date());

2.用一个格式化字符串指出要格式化的参数索引。索引必须紧跟在%后面,并以$终止。

1
System.out.printf("%1$s %2$tB %2$te, %2$tY", "Due date:", new Date());

3.可使用<标志。它指示前面格式说明中的参数将被再次使用。

1
System.out.printf("%s %tB %<te, %<tY", "Due date:", new Date());

注意:参数索引值从1开始,%1$…对第1个参数格式化。

4.数字和日期的格式化规则是特定化于本地化环境的,如中国October被格式化为十月。

Conversion Character Type Example
c Complete date and time Mon Feb 09 18:05:19 PST 2015
F ISO 8601 date 2015-02-09
D U.S. formatted date (month/day/year) 02/09/2015
T 24-hour time 18:05:19
r 12-hour time 06:05:19 pm
R 24-hour time, no seconds 18:05
Y Four-digit year (with leading zeroes) 2015
y Last two digits of the year (with leading 15 zeroes) 15
C First two digits of the year (with leading 20 zeroes) 20
B Full month name February
b or h Abbreviated month name Feb
m Two-digit month (with leading zeroes) 02
d Two-digit day (with leading zeroes) 09
e Two-digit day (without leading zeroes) 9
A Full weekday name Monday
a Abbreviated weekday name Mon
j Three-digit day of year (with leading 069 zeroes), between 001 and 366 069
H Two-digit hour (with leading zeroes), between 00 and 23 18
k Two-digit hour (without leading zeroes), between 0 and 23 18
Conversion Character Type Example
I Two-digit hour (with leading zeroes), between 01 and 12 06
l Two-digit hour (without leading zeroes), between 1 and 12 6
M Two-digit minutes (with leading zeroes) 05
S Two-digit seconds (with leading zeroes) 19
L Three-digit milliseconds (with leading zeroes) 047
N Nine-digit nanoseconds (with leading zeroes) 047000000
p Morning or afternoon marker pm
z RFC 822 numeric offset from GMT -0800
Z Time zone PST
s Seconds since 1970–01–01 00:00:00 GMT 1078884319
Q Milliseconds since 1970–01–01 00:00:00 GMT 1078884319047

文件输入与输出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import java.io.IOException;
import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.util.Scanner;

public class FileOperation {
public static void main(String[] args) throws IOException {
Scanner in =new Scanner(Path.of("c:\\myfile.txt"),StandardCharsets.UTF_8);
while(in.hasNext()) {
String name = in.next();
int score = in.nextInt();
System.out.println(name+":"+score);
}
in.close();

PrintWriter out = new PrintWriter("D:\\filetest.txt",StandardCharsets.UTF_8);
out.print("Hello");
out.println(" Java World");
out.println("Java Core");
out.close();
}
}

1.对文件进行读取,需要构造一个Scanner对象。

2.如果文件名中包含反斜杠符号,在每个反斜杠之前再加一个额外的反斜杠转义。

3.写入文件需要构造一个PrintWriter对象,在构造器中,需要提供文件名和字符编码。

4.当构造带有字符串参数的Scanner时,会将字符串解释为数据,而不是文件名。


1.当指定一个相对文件名时,文件位于相对于Java虚拟机启动目录的位置。

如"myfile.txt","mydirectory/myfile.txt"或"../myfile.txt"

  • 命令行方式下用下列命令启动程序:
1
java MyProg

启动路径就是命令解释器的当前路径。

  • 使用集成开发环境,启动路径由IDE控制,使用如下调用找到该目录位置:
1
String dir = System.getProperty("user.dir");
  • 可以使用绝对路径

如"c:\mydirectory\myfile.txt"或者"/home/me/mydirectory/myfile.txt"

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import java.io.IOException;
import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.util.Scanner;

public class FileOperation {
/*
如果用一个不存在的文件构造一个Scanner,或者用一个不能被创建的文件名构造一个PrintWriter,会发生异常,需要在main方法中用throws子句标记
当采用命令行方式启动一个程序时,可以利用Shell的重定向语法将任意文件关联到System.in和System.out
java MyProg <myfile.txt> output.txt
即无需处理IOException异常
*/
public static void main(String[] args) throws IOException {
String dir = System.getProperty("user.dir");
System.out.println(dir); /*dir/myfile.txt*/
Scanner in =new Scanner(Path.of("myfile.txt"),StandardCharsets.UTF_8);
while(in.hasNext()) {
String name = in.next();
int score = in.nextInt();
System.out.println(name+":"+score);
}
in.close();
}
}