JAVA基础
一、变量
(一)、基本变量类型
byte 1字节 short 2字节 int 4字节 long 8字节
char 2字节 float 4字节 double 8字节 boolean 1字节
①整型
整型变量用于存放整数。区别在于不同类型的整型,最大值,最小值不一样。
如果试图给byte类型的变量赋予超出其范围的值,就会产生编译错误,这个时候就需要进行类型转换。
②字符型
char类型用于存放一个字符,值用单引号’表示 (双引号表示字符串)。其长度和short一样,也是16位的。只能存放一个字符,超过一个字符就会产生编译错误
③浮点型
浮点数类型有两种:
float 长度为32位
double 长度为64位
注意: 默认的小数值是double类型的。
所以 float f = 54.321会出现编译错误,因为54.321的默认类型是 double,其类型 长度为64,超过了float的长度32。在数字后面加一个字母f,直接把该数字声明成float类型
float f2 = 54.321f。
④布尔型
布尔型用于表示真假,其长度为1。
虽然布尔型真正存放的数据是0(false) 1(true)。但是,不能直接使用0 1 进行赋值
(二)、字面值
给基本类型的变量赋值的方式叫做 字面值。
①整数的字面值
当以l或者L结尾的时候,一个整数字面值是long类型,否则就是int类型。 建议使用大写的L而非小写的l,因为容易和1混淆。
byte,short,int和long的值都可以通过int类型的字面值来创建。整数的字面值可以用如下四种进制来表示:
十进制: 基 10, 包含从0-9的数字,平常用的就是这种
十六进制: 基 16, 包含从0-9的数字,和从A-F的字母。
八进制: 基 8, 包含从0-7的数字
二进制: 基 2, 包含0和1。
public class HelloWorld {
public static void main(String[] args) {
long val = 26L; //以L结尾的字面值表示long型
int decVal = 26; //默认就是int型
int hexVal = 0x1a; //16进制
int oxVal = 032; //8进制
int binVal = 0b11010; //2进制
System.out.println(oxVal);
}
}
②浮点数字面值
当以f或者F结尾的时候,就表示一个float类型的浮点数,否则就是double类型(以d或者D结尾,写不写都可以)。
浮点数还可以用E或者e表示(科学计数法)e2表示10的二次方,即100。
1.234e2 = 1.234x100
public class HelloWorld {
public static void main(String[] args) {
float f1 = 123.4F;// 以F结尾的字面值表示float类型
double d1 = 123.4;// 默认就是double类型
double d2 = 1.234e2;// 科学计数法表示double
}
}
③字符和字符串字面值
字符的字面值放在单引号中。
字符串的字面值放在双引号中。
需要注意的是,\表示转义,比如需要表示制表符,回车换行,双引号等就需要用 \t \r \n " 的方式进行。
public class HelloWorld {
public static void main(String[] args) {
String name = "盖伦";
char a= 'c';
//以下是转义字符
char tab = '\t'; //制表符
char carriageReturn = '\r'; //回车
char newLine = '\n'; //换行
char doubleQuote = '\"'; //双引号
char singleQuote = '\''; //单引号
char backslash = '\\'; //反斜杠
}
}
(三)、类型转换
①自动转换
小范围的可以自动转换为大范围的
-
低精度向高精度转换:
l 是long类型的,其类型长度是64位。i 是int类型的,其类型长度是32位
所以l的精度,比i的精度要高。
l = i;
把i的值赋给l, 首先l和i彼此的类型是不一样的,那么能否转换就取决于彼此的精度。这个例子,是低精度向高精度转换 是可以正常转换的。换句话说,int比较小,要放进比较大的long,随便怎么样,都放的进去。
- 高精度向低精度转换
b的类型是byte,其长度是8,最大只能放127。i1 的类型是int, 其长度是32,最大,反正就是很大了,超过127。所以, 把int类型的数据转成为byte类型的数据,是有风险的。有的时候是可以转换的,比如 b = i1 (i1=10);有的时候不可以转换 比如 b= i2 (i2=300) 因为放不下了,编译器就会提示错误。这个时候就只能采用强制转换。
②强制转换
int a = (int)5555L;
short/char/byte可以进行加法运算,但会在运算时将数据自动转换为int类型再运算,结果为int 类型。
public class HelloWorld {
public static void main(String[] args) {
byte b = 5;
int i1 = 10;
int i2 = 300;
b = (byte) i1;
//因为i1的值是在byte范围之内,所以即便进行强制转换
//最后得到的值,也是10
System.out.println(b);
//因为i2的值是在byte范围之外,所以就会按照byte的长度进行截取
//i2的值是300,其对应的二进制数是 100101100
//按照byte的长度8位进行截取后,其值为 00101100 即44
b =(byte) i2;
System.out.println(b);
//查看一个整数对应的二进制的方法:
System.out.println(Integer.toBinaryString(i2));
}
}
(四)、变量命名规则
-
变量命名只能使用字母 数字 $ _
-
变量第一个字符 只能使用 字母 $ _
-
不能只使用关键字,但是可以包含关键字
(五)、作用域
变量处于不同的位置,有不同的名称,分别是:
字段,属性
参数
局部变量
不同名称的变量,其作用域是不一样的。
①字段,属性,Field
当一个变量被声明在类下面,变量就叫做字段 或者属性、成员变量、Field
比如变量i,就是一个属性,那么从第2行这个变量声明的位置开始,整个类都可以访问得到,所以其作用域就是从其声明的位置开始的整个类。
public class HelloWorld {
int i = 1;
int j = i; //其他的属性可以访问i
public void method1(){
System.out.println(i); //方法1里可以访问i
}
public void method2(){
System.out.println(i); //方法2里可以访问i
}
}
②参数
如果一个变量,是声明在一个方法上的,就叫做参数。参数的作用域即为该方法内的所有代码,其他方法不能访问该参数,类里面也不能访问该参数。
public class HelloWorld {
public void method1(int i){ //参数i的作用域即方法method1
System.out.println(i);
}
public void method2(){
System.out.println(i); //method2 不能访问参数i
}
int j = i; //类里面也不能访问参数i
}
③局部变量
声明在方法内的变量,叫做局部变量。其作用域在声明开始的位置,到其所处于的块结束位置。
public class HelloWorld {
public void method1() {
int i = 5; //其作用范围是从声明的第4行,到其所处于的块结束12行位置
System.out.println(i);
{ //子块
System.out.println(i); //可以访问i
int j = 6;
System.out.println(j); //可以访问j
}
System.out.println(j); //不能访问j,因为其作用域到第10行就结束了
}
}
(六)、变量final
final 修饰一个变量,有很多种说法,比如不能改变等等。
准确的描述是,当一个变量被final修饰的时候,该变量只有一次赋值的机会。
-
在声明的时候赋值:
public class HelloWorld { public void method1() { final int i = 5; i = 10; //i在第4行已经被赋值过了,所以这里会出现编译错误 } }
-
在声明时没有赋值:
public class HelloWorld { public void method1() { final int i; i = 10; //i在第4行,只是被声明,但是没有被赋值,所以在这里可以进行第一次赋值 i = 11; //i在第6行已经被赋值过了,所以这里会出现编译错误 } }
-
final修饰:
- final:修饰类,无法被继承
- final修饰方法,不能被子类重写
- final修饰变量,该变量为常量,只能赋值一次,不能被修改
二、操作符
(一)、算术操作符
①基本算术操作符
+ - * /
public class HelloWorld {
public static void main(String[] args) {
int i = 10;
int j = 5;
int a = i+j;
int b = i - j;
int c = i*j;
int d = i /j;
}
}
如果有任何运算单元的长度超过int,那么运算结果就按照最长的长度计算
public class HelloWorld {
public static void main(String[] args) {
int a = 5;
long b = 6;
int c = (int) (a+b); //a+b的运算结果是long型,所以要进行强制转换
long d = a+b;
}
}
如果任何运算单元的长度都不超过int,那么运算结果就按照int来计算
public class HelloWorld {
public static void main(String[] args) {
byte a = 1;
byte b= 2;
byte c = (byte) (a+b); //虽然a b都是byte类型,但是运算结果是int类型,需要进行强制转换
int d = a+b;
}
}
②取模
% 取余数,又叫取模
public class HelloWorld {
public static void main(String[] args) {
int i = 5;
int j = 2;
System.out.println(i%j); //输出为1
}
}
③自增自减
++
–
在原来的基础上增加1或者减少1
public class HelloWorld {
public static void main(String[] args) {
int i = 5;
i++;
System.out.println(i);//输出为6
}
}
自增 自减操作符置前以及置后的区别
以++为例
int i = 5;
i++; 先取值,再运算
++i; 先运算,再取值
public class HelloWorld {
public static void main(String[] args) {
int i = 5;
System.out.println(i++); //输出5
System.out.println(i); //输出6
int j = 5;
System.out.println(++j); //输出6
System.out.println(j); //输出6
}
}
(二)、关系操作符
关系操作符:比较两个变量之间的关系
> 大于
>= 大于或等于
< 小于
<= 小于或等于
== 是否相等
!= 是否不等
public class HelloWorld {
public static void main(String[] args) {
int a = 5;
int b = 6;
int c = 5;
System.out.println(a>b); //返回 false
System.out.println(a>=c); //返回 true
System.out.println(a==b); //返回false
System.out.println(a!=b);//返回true
}
}
(三)、逻辑操作符
①长路与和短路与
无论长路与还是短路与,两边的运算单元都是布尔值,都为真时,才为真,任意为假,就为假。
区别
长路与 两侧,都会被运算
短路与 只要第一个是false,第二个就不进行运算了
public class HelloWorld {
public static void main(String[] args) {
//长路与 无论第一个表达式的值是true或者false,第二个的值,都会被运算
int i = 2;
System.out.println( i== 1 & i++ ==2 ); //无论如何i++都会被执行,所以i的值变成了3
System.out.println(i);
//短路与 只要第一个表达式的值是false的,第二个表达式的值,就不需要进行运算了
int j = 2;
System.out.println( j== 1 && j++ ==2 ); //因为j==1返回false,所以右边的j++就没有执行了,所以j的值,还是2
System.out.println(j);
}
}
②长路或和短路或
无论长路或还是短路或,两边的运算单元都是布尔值,都为假时,才为假,任意为真,就为真。
区别:
长路或 两侧都会被运算
短路或 只要第一个是true的,第二个就不进行运算了
public class HelloWorld {
public static void main(String[] args) {
//长路或 无论第一个表达式的值是true或者false,第二个的值,都会被运算
int i = 2;
System.out.println( i== 1 | i++ ==2 ); //无论如何i++都会被执行,所以i的值变成了3
System.out.println(i);
//短路或 只要第一个表达式的值是true的,第二个表达式的值,就不需要进行运算了
int j = 2;
System.out.println( j== 2 || j++ ==2 ); //因为j==2返回true,所以右边的j++就没有执行了,所以j的值,还是2
System.out.println(j);
}
}
③取反
!真变为假,假变为真。
public class HelloWorld {
public static void main(String[] args) {
boolean b = true;
System.out.println(b); //输出true
System.out.println(!b);//输出false
}
}
④异或
异或^,不同,返回真,相同,返回假。
public class HelloWorld {
public static void main(String[] args) {
boolean a = true;
boolean b = false;
System.out.println(a^b); //不同返回真
System.out.println(a^!b); //相同返回假
}
}
(四)、位操作符
①一个整数的二进制表达
位操作都是对二进制而言的,但是我们平常使用的都是十进制比如5。而5的二进制是101。所以在开始学习之前,需要掌握一个整数的二进制表达是多少。通过Integer.toBinaryString() 方法,将一个十进制整数转换为一个二进制字符串。
public class HelloWorld {
public static void main(String[] args) {
int i = 5;
String b = (Integer.toBinaryString(i)); // 5的二进制的表达101
System.out.println(i+" 的二进制表达是: "+b);
}
}
②位或
5的二进制是101,6的二进制是110,所以 5|6 对每一位进行或运算,得到 111->7。
public class HelloWorld {
public static void main(String[] args) {
int i =5;
int j = 6;
System.out.println(Integer.toBinaryString(i)); //5的二进制是101
System.out.println(Integer.toBinaryString(j)); //6的二进制是110
System.out.println(i|j); //所以 5|6 对每一位进行或运算,得到 111->7
}
}
③位与
5的二进制是101,6的二进制是110,所以 5&6 对每一位进行与运算,得到 100->4。
public class HelloWorld {
public static void main(String[] args) {
int i =5;
int j = 6;
System.out.println(Integer.toBinaryString(i)); //5的二进制是101
System.out.println(Integer.toBinaryString(j)); //6的二进制是110
System.out.println(i&j); //所以 5&6 对每一位进行与运算,得到 100->4
}
}
④异或
5的二进制是101,6的二进制是110,所以 5^6 对每一位进行异或运算,得到 011->3。
一些特别情况:
任何数和自己进行异或 都等于 0
任何数和0 进行异或 都等于自己
public class HelloWorld {
public static void main(String[] args) {
int i =5;
int j = 6;
System.out.println(Integer.toBinaryString(i)); //5的二进制是 101
System.out.println(Integer.toBinaryString(j)); //6的二进制是110
System.out.println(i^j); //所以 5^6 对每一位进行或运算,得到 011->3
System.out.println(i^0);
System.out.println(i^i);
}
}
⑤取非
5 的二进制是 00000101,所以取反即为 11111010,这个二进制换算成十进制即为-6
public class HelloWorld {
public static void main(String[] args) {
byte i =5;
System.out.println(Integer.toBinaryString(i)); //5的二进制是00000101,所以取非即为11111010,即为-6
System.out.println(~i);
}
}
⑥左移右移
左移:根据一个整数的二进制表达,将其每一位都向左移动,最右边一位补0
右移:根据一个整数的二进制表达,将其每一位都向右移动
public class HelloWorld {
public static void main(String[] args) {
byte i =6;
//6的二进制是110
System.out.println(Integer.toBinaryString(i));
//6向左移1位后,变成1100,对应的10进制是12
System.out.println(i<<1);
//6向右移1位后,变成11,对应的10进制是3
System.out.println(i>>1);
}
}
⑦带符号右移与无符号右移
带符号右移 >>
对于正数, 带符号右移 >> 会把所有的位右移,并在最前面补0
对于负数, 带符号右移 >> 会把所有的位右移,并在最前面补1
无符号右移>>>
如果是一个负数,那么对应的二进制的第一位是1
无符号右移>>>会把第一位的1也向右移动,导致移动后,第一位变成0
这样就会使得负数在无符号右移后,得到一个正数
简单的说:
带符号右移 >> 移动后正的还是正的,负的还是负的,符号不变
**无符号右移>>>**移动后,变正的了
public class HelloWorld {
public static void main(String[] args) {
int i =-10;
//-10的二进制是11111111111111111111111111110110
//第一位是1,即符号位,代表这是一个负数
System.out.println(Integer.toBinaryString(i));
//对于正数, 带符号右移 >> 会把所有的位右移,并在最前面补0
//对于负数, 带符号右移 >> 会把所有的位右移,并在最前面补1
//-10带符号右移1位,移动后前面补齐1
//得到11111111111111111111111111111011
//因为第一位是1,所以依然是一个负数,对应的十进制是-5
int j = i>>1;
System.out.println(Integer.toBinaryString(j));
System.out.println(j);
//-10无符号向右移1位,符号位也会向右移,第一位就变成了0
//得到01111111111111111111111111111011,对应的十进制是2147483643
int k = i>>>1;
System.out.println(Integer.toBinaryString(k));
System.out.println(k);
}
}
(五)、赋值操作符
(六)、三元操作符
表达式?值1:值2
如果表达式为真 返回值1
如果表达式为假 返回值2
public class HelloWorld {
public static void main(String[] args) {
int i = 5;
int j = 6;
int k = i < j ? 99 : 88;
// 相当于
if (i < j) {
k = 99;
} else {
k = 88;
}
System.out.println(k);
}
}
(七)、Scanner
①使用Scanner读取整数
import java.util.Scanner;
public class HelloWorld {
public static void main(String[] args) {
Scanner s = new Scanner(System.in);
int a = s.nextInt();
System.out.println("第一个整数:"+a);
int b = s.nextInt();
System.out.println("第二个整数:"+b);
}
}
②使用Scanner读取浮点数
import java.util.Scanner;
public class HelloWorld {
public static void main(String[] args) {
Scanner s = new Scanner(System.in);
float a = s.nextFloat();
System.out.println("读取的浮点数的值是:"+a);
}
}
③使用Scanner读取字符串
import java.util.Scanner;
public class HelloWorld {
public static void main(String[] args) {
Scanner s = new Scanner(System.in);
String a = s.nextLine();
System.out.println("读取的字符串是:"+a);
}
}
④使用Scanner读取字符串
import java.util.Scanner;
public class HelloWorld {
public static void main(String[] args) {
Scanner s = new Scanner(System.in);
String a = s.nextLine();
System.out.println("读取的字符串是:"+a);
}
}
⑤读取整数后接着读取字符串
需要注意的是,如果在通过nextInt()读取了整数后,再接着读取字符串,读出来的是回车换行:"\r\n",因为nextInt仅仅读取数字信息,而不会读取回车换行"\r\n"。
所以,如果在业务上需要读取了整数后,接着读取字符串,那么就应该连续执行两次nextLine(),第一次是取走回车换行,第二次才是读取真正的字符串。
import java.util.Scanner;
public class HelloWorld {
public static void main(String[] args) {
Scanner s = new Scanner(System.in);
int i = s.nextInt();
System.out.println("读取的整数是"+ i);
String rn = s.nextLine();
String a = s.nextLine();
System.out.println("读取的字符串是:"+a);
}
}
三、数组
数组是一个固定长度的,包含了相同类型数据的 容器
(一)、创建数组
①动态初始化数组
-
基本格式:数据类型[] 数组名 = new 数据类型[数组长度];
int[] a = new int[9];
-
拆分格式:
int a; a = new int[9];
②静态初始化数组
-
省略格式:数据类型[] 数组名 = {};
int[] a = {4,3,8};
-
拆分格式:
int a; a = new int[]{4,2,6};
③使用动态创建数组时,数组中的默认值
-
整数:0
-
浮点数:0.0
-
字符型:’\u0000’
-
布尔型:false
-
引用类型:null
静态初始化也有默认值,程序会立即将默认值替换为大括号中的值
④数组长度:.length属性
.length属性用于访问一个数组的长度,数组访问下标范围是0到长度-1,一旦超过这个范围,就会产生数组下标越界异常。
public class HelloWorld {
public static void main(String[] args) {
int[] a;
a = new int[5];
System.out.println(a.length); //打印数组的长度
a[4]=100; //下标4,实质上是“第5个”,即最后一个
a[5]=101; //下标5,实质上是“第6个”,超出范围 ,产生数组下标越界异常
}
}
(二)、增强for循环
增强型for循环只能用来取值,却不能用来修改数组里的值
格式:
for(元素数据类型变量名:数组或者Collection集合){
//在此处使用变量即可,该变量就是元素
}
例:
int[] arr = {1,2,3,4,5};
for(int i:arr){
System.out.println(i);
}
(三)、复制数组
public class HelloWorld {
public static void main(String[] args) {
int a [] = new int[]{18,62,68,82,65,9};
int b[] = new int[3];//分配了长度是3的空间,但是没有赋值
//通过数组赋值把,a数组的前3位赋值到b数组
//方法一: for循环
for (int i = 0; i < b.length; i++) {
b[i] = a[i];
}
//方法二: System.arraycopy(src, srcPos, dest, destPos, length)
//src: 源数组
//srcPos: 从源数组复制数据的起始位置
//dest: 目标数组
//destPos: 复制到目标数组的启始位置
//length: 复制的长度
System.arraycopy(a, 0, b, 0, 3);
//把内容打印出来
for (int i = 0; i < b.length; i++) {
System.out.print(b[i] + " ");
}
}
}
(四)、二维数组
这是一个二维数组,里面的每一个元素,都是一个一维数组,所以二维数组又叫数组的数组。
int b[][] = new int[][]{
{1,2,3},
{4,5,6},
{7,8,9}
};
初始化二维数组:
public class HelloWorld {
public static void main(String[] args) {
//初始化二维数组,
int[][] a = new int[2][3]; //有两个一维数组,每个一维数组的长度是3
a[1][2] = 5; //可以直接访问一维数组,因为已经分配了空间
//只分配了二维数组
int[][] b = new int[2][]; //有两个一维数组,每个一维数组的长度暂未分配
b[0] =new int[3]; //必须事先分配长度,才可以访问
b[0][2] = 5;
//指定内容的同时,分配空间
int[][] c = new int[][]{
{1,2,4},
{4,5},
{6,7,8,9}
};
}
}
(五)、数组Arrays类
Arrays是针对数组的工具类,可以进行 排序,查找,复制填充等功能。 大大提高了开发人员的工作效率。
①数组复制
与使用System.arraycopy进行数组复制类似的, Arrays提供了一个copyOfRange方法进行数组复制。
不同的是System.arraycopy,需要事先准备好目标数组,并分配长度。 copyOfRange 只需要源数组就就可以了,通过返回值,就能够得到目标数组了。
除此之外,需要注意的是 copyOfRange 的第3个参数,表示源数组的结束位置,是取不到的。
import java.util.Arrays;
public class HelloWorld {
public static void main(String[] args) {
int a[] = new int[] { 18, 62, 68, 82, 65, 9 };
// copyOfRange(int[] original, int from, int to)
// 第一个参数表示源数组
// 第二个参数表示开始位置(取得到)
// 第三个参数表示结束位置(取不到)
int[] b = Arrays.copyOfRange(a, 0, 3);
for (int i = 0; i < b.length; i++) {
System.out.print(b[i] + " ");
}
}
}
②转换为字符串
Arrays.toString(arrayName);
例:
import java.util.Arrays;
public class HelloWorld {
public static void main(String[] args) {
int a[] = new int[] { 18, 62, 68, 82, 65, 9 };
String content = Arrays.toString(a);
System.out.println(content);
}
}
③升序排序
Arrays.sort(Object[] array);
Arrays.sort(Object[] array, int from, int to);//对数组元素指定范围进行排序
//(排序范围是从元素下标为from,到下标为to-1的元素进行排序)
例:
import java.util.Arrays;
public class HelloWorld {
public static void main(String[] args) {
int a[] = new int[] { 18, 62, 68, 82, 65, 9 };
System.out.println("排序之前 :");
System.out.println(Arrays.toString(a));
Arrays.sort(a);
System.out.println("排序之后:");
System.out.println(Arrays.toString(a));
}
}
④搜索
查询元素出现的位置。
需要注意的是,使用binarySearch进行查找之前,必须使用sort进行排序。
如果数组中有多个相同的元素,查找结果是不确定的。
import java.util.Arrays;
public class HelloWorld {
public static void main(String[] args) {
int a[] = new int[] { 18, 62, 68, 82, 65, 9 };
Arrays.sort(a);
System.out.println(Arrays.toString(a));
//使用binarySearch之前,必须先使用sort进行排序
System.out.println("数字 62出现的位置:"+Arrays.binarySearch(a, 62));
}
}
⑤判断是否相同
import java.util.Arrays;
public class HelloWorld {
public static void main(String[] args) {
int a[] = new int[] { 18, 62, 68, 82, 65, 9 };
int b[] = new int[] { 18, 62, 68, 82, 65, 8 };
System.out.println(Arrays.equals(a, b));
}
}
⑥填充
import java.util.Arrays;
public class HelloWorld {
//使用同一个值,填充整个数组
public static void main(String[] args) {
int a[] = new int[10];
Arrays.fill(a, 5);
System.out.println(Arrays.toString(a));
}
}
四、类和对象
(一)、引用
引用的概念,如果一个变量的类型是 类类型,而非基本类型,那么该变量又叫做引用。
(二)、继承
①super()和this()
- super();子类调用父类的构造器,只能放在子类构造方法中的第一行
- this()调用同类中的构造方法,只能放在第一行
- 子类父类拥有同名变量,采用就近原则,访问参数用name,访问子类中的变量用this.name,访问父类中的用super.name
②方法重写override:
子类中可以有和父类名称相同的方法,但子类权限不能低于父类。子类私有父类公有是不允许的。
③方法重载overload:
方法的重载指的是方法名一样,但是参数类型不一样
④final关键字
- final:修饰类,无法被继承
- final修饰方法,不能被子类重写
- final修饰变量,该变量为常量,只能赋值一次,不能被修改
(三)、构造方法
通过一个类创建一个对象,这个过程叫做实例化。实例化是通过调用构造方法(又叫做构造器)实现的。
构造方法方法名和类名一样(包括大小写)。没有返回类型,实例化一个对象的时候,必然调用构造方法。
public class Hero {
String name;
float hp;
float armor;
int moveSpeed;
// 方法名和类名一样(包括大小写)
// 没有返回类型
public Hero() {
System.out.println("实例化一个对象的时候,必然调用构造方法");
}
public static void main(String[] args) {
//实例化一个对象的时候,必然调用构造方法
Hero h = new Hero();
}
}
如果不写构造方法,会默认提供一个隐式的无参构造方法。如果写了构造方法,则不会默认提供。
(四)、访问修饰符
①类使用的修饰符
- public(可在包内和包外使用)
- 默认default(只能在包内使用)
②成员变量
- private成员:只能在本类中访问
- public成员:所有地方都可访问
- 默认default成员:同一个包中可以访问,子父类不能访问
- protect成员:不同包之间不能访问,子父类中可以访问
(五)、static关键字
当一个属性被static修饰的时候,就叫做类属性,又叫做静态属性。
类方法: 又叫做静态方法。访问一个对象方法,必须建立在有一个对象的前提的基础上。访问类方法,不需要对象的存在,直接就访问。
- static 静态变量。在内存区只有一个,是公用的变量;
- static静态方法。不能使用非静态的变量。
package charactor;
public class Hero {
public String name;
protected float hp;
//实例方法,对象方法,非静态方法
//必须有对象才能够调用
public void die(){
hp = 0;
}
//类方法,静态方法
//通过类就可以直接调用
public static void battleWin(){
System.out.println("battle win");
}
public static void main(String[] args) {
Hero garen = new Hero();
garen.name = "盖伦";
//必须有一个对象才能调用
garen.die();
Hero teemo = new Hero();
teemo.name = "提莫";
//无需对象,直接通过类调用
Hero.battleWin();
}
}
(六)、属性初始化
①对象属性初始化
对象属性初始化有3种:
- 声明该属性的时候初始化;
- 构造方法中初始化;
- 初始化块。
package charactor;
public class Hero {
public String name = "some hero"; //声明该属性的时候初始化
protected float hp;
float maxHP;
{
maxHP = 200; //初始化块
}
public Hero(){
hp = 100; //构造方法中初始化
}
}
②类属性初始化
类属性初始化有2种:
- 声明该属性的时候初始化;
- 静态初始化块。
package charactor;
public class Hero {
public String name;
protected float hp;
float maxHP;
//物品栏的容量
public static int itemCapacity=8; //声明的时候 初始化
static{
itemCapacity = 6;//静态初始化块 初始化
}
public Hero(){
}
public static void main(String[] args) {
System.out.println(Hero.itemCapacity);
}
}
(七)、枚举类型
枚举enum是一种特殊的类(还是类),使用枚举可以很方便的定义常量。一个常用的场合就是switch语句中,使用枚举来进行判断。
public enum Season {
SPRING,SUMMER,AUTUMN,WINTER
}
public class HelloWorld {
public static void main(String[] args) {
Season season = Season.SPRING;
switch (season) {
case SPRING:
System.out.println("春天");
break;
case SUMMER:
System.out.println("夏天");
break;
case AUTUMN:
System.out.println("秋天");
break;
case WINTER:
System.out.println("冬天");
break;
}
}
}
借助增强型for循环,可以很方便的遍历一个枚举都有哪些常量
public class HelloWorld {
public static void main(String[] args) {
for (Season s : Season.values()) {
System.out.println(s);
}
}
}
(八)、内部类
①非静态内部类
非静态内部类 BattleScore “战斗成绩”,非静态内部类可以直接在一个类里面定义,
比如:
战斗成绩只有在一个英雄对象存在的时候才有意义,所以实例化BattleScore 的时候,必须建立在一个存在的英雄的基础上。
语法: new 外部类().new 内部类()
作为Hero的非静态内部类,是可以直接访问外部类的private实例属性name的。
package charactor;
public class Hero {
private String name; // 姓名
float hp; // 血量
float armor; // 护甲
int moveSpeed; // 移动速度
// 非静态内部类,只有一个外部类对象存在的时候,才有意义
// 战斗成绩只有在一个英雄对象存在的时候才有意义
class BattleScore {
int kill;
int die;
int assit;
public void legendary() {
if (kill >= 8)
System.out.println(name + "超神!");
else
System.out.println(name + "尚未超神!");
}
}
public static void main(String[] args) {
Hero garen = new Hero();
garen.name = "盖伦";
// 实例化内部类
// BattleScore对象只有在一个英雄对象存在的时候才有意义
// 所以其实例化必须建立在一个外部类对象的基础之上
BattleScore score = garen.new BattleScore();
score.kill = 9;
score.legendary();
}
}
②静态内部类
在一个类里面声明一个静态内部类。
比如敌方水晶,当敌方水晶没有血的时候,己方所有英雄都取得胜利,而不只是某一个具体的英雄取得胜利。
与非静态内部类不同,静态内部类水晶类的实例化 不需要一个外部类的实例为基础,可以直接实例化。
语法:new 外部类.静态内部类();
因为没有一个外部类的实例,所以在静态内部类里面不可以访问外部类的实例属性和方法。
除了可以访问外部类的私有静态成员外,静态内部类和普通类没什么大的区别。
package charactor;
public class Hero {
public String name;
protected float hp;
private static void battleWin(){
System.out.println("battle win");
}
//敌方的水晶
static class EnemyCrystal{
int hp=5000;
//如果水晶的血量为0,则宣布胜利
public void checkIfVictory(){
if(hp==0){
Hero.battleWin();
//静态内部类不能直接访问外部类的对象属性
System.out.println(name + " win this game");
}
}
}
public static void main(String[] args) {
//实例化静态内部类
Hero.EnemyCrystal crystal = new Hero.EnemyCrystal();
crystal.checkIfVictory();
}
}
③匿名类
匿名类指的是在声明一个类的同时实例化它,使代码更加简洁精练。
通常情况下,要使用一个接口或者抽象类,都必须创建一个子类,有的时候,为了快速使用,直接实例化一个抽象类,并“当场”实现其抽象方法。
既然实现了抽象方法,那么就是一个新的类,只是这个类,没有命名。
这样的类,叫做匿名类。
package charactor;
public abstract class Hero {
String name; //姓名
float hp; //血量
float armor; //护甲
int moveSpeed; //移动速度
public abstract void attack();
public static void main(String[] args) {
ADHero adh=new ADHero();
//通过打印adh,可以看到adh这个对象属于ADHero类
adh.attack();
System.out.println(adh);
Hero h = new Hero(){
//当场实现attack方法
public void attack() {
System.out.println("新的进攻手段");
}
};
h.attack();
//通过打印h,可以看到h这个对象属于Hero$1这么一个系统自动分配的类名
System.out.println(h);
}
}
④本地类
本地类可以理解为有名字的匿名类。内部类与匿名类不一样的是,内部类必须声明在成员的位置,即与属性和方法平等的位置。本地类和匿名类一样,直接声明在代码块里面,可以是主方法,for循环里等等地方。
package charactor;
public abstract class Hero {
String name; //姓名
float hp; //血量
float armor; //护甲
int moveSpeed; //移动速度
public abstract void attack();
public static void main(String[] args) {
//与匿名类的区别在于,本地类有了自定义的类名
class SomeHero extends Hero{
public void attack() {
System.out.println( name+ " 新的进攻手段");
}
}
SomeHero h =new SomeHero();
h.name ="地卜师";
h.attack();
}
}
五、抽象类和接口
(一)、抽象类
abstract class xxx{}
- 可包含正常方法和抽象方法,可以包含构造方法但不能实例化。
- 其子类可以为抽象类(不必实现父类中的抽象方法),子类为正常类时必须实现父类抽象类中的抽象方法。
(二)、接口
- 接口是完全抽象化的类
- 接口可被其它类(包括抽象类)实现
- 接口不能被继承(接口只定义一些东西,没有可被继承的数据或者方法
①定义
interface A{
void method1();********默认为抽象方法,省略public abstract
void method2();
}
② 实现
class B implements A{
public void method1(){
System.out.println("方法一实现了");
}
public void method2(){
System.out.println("方法二实现了");
}
}
③接口当中的默认方法
可以解决接口升级的问题
public default 返回值类型 方法名称(参数列表){方法体};
④接口中的静态方法
public static 返回值类型 方法名称(参数){方法体};
与对象无关,所以使用格式:接口名称.静态方法名称(参数);
⑤接口中的常量
-
必须使用public static final进行修饰(这三个修饰词可省略),
例:public static final int a = 0;
-
定义时必须赋值;
-
常量中的名称全部用大写字母,用下划线进行分割。
⑥注意事项
- 接口没有静态代码块和构造方法
- 一个类的直接父类是唯一的,但可以同时实现多个接口
- 如果实现类所实现的多个接口中有重复的抽象方法,只需要覆盖重写一次即可
- 如果实现类没有覆盖重写所有接口中的所有抽象方法,那么该实现类就必须是一个抽象类
- 如果实现类实现的多个方法中,有重复名称的多个默认方法,那么实现类必须对该默认方法进行覆盖重写
- 一个实现类如果它的直接父类中的方法与接口中的默认方法产生了冲突,优先使用父类中的方法
- 接口之间可以多继承。多个父接口如果有重复的默认方法,子接口必须对其进行覆盖重写
(三)、Object类
Object类是所有类的父类,声明一个类的时候,默认是继承了Object。
①toString()
Object类提供一个toString方法,所以所有的类都有toString方法,toString()的意思是返回当前对象的字符串表达,通过 System.out.println 打印对象就是打印该对象的toString()返回值。
package charactor;
public class Hero {
public String name;
protected float hp;
public String toString(){
return name;
}
public static void main(String[] args) {
Hero h = new Hero();
h.name = "盖伦";
System.out.println(h.toString());
//直接打印对象就是打印该对象的toString()返回值
System.out.println(h);
}
}
②finalize()
当一个对象没有任何引用指向的时候,它就满足垃圾回收的条件,当它被垃圾回收的时候,它的finalize() 方法就会被调用。,finalize() 不是开发人员主动调用的方法,而是由虚拟机JVM调用的。
package charactor;
public class Hero {
public String name;
protected float hp;
public String toString(){
return name;
}
public void finalize(){
System.out.println("这个英雄正在被回收");
}
public static void main(String[] args) {
//只有一引用
Hero h;
for (int i = 0; i < 100000; i++) {
//不断生成新的对象
//每创建一个对象,前一个对象,就没有引用指向了
//那些对象,就满足垃圾回收的条件
//当,垃圾堆积的比较多的时候,就会触发垃圾回收
//一旦这个对象被回收,它的finalize()方法就会被调用
h = new Hero();
}
}
}
③equals()
equals() 用于判断两个对象的内容是否相同。
package charactor;
public class Hero {
public String name;
protected float hp;
public boolean equals(Object o){
if(o instanceof Hero){
Hero h = (Hero) o;
return this.hp == h.hp;
}
return false;
}
public static void main(String[] args) {
Hero h1= new Hero();
h1.hp = 300;
Hero h2= new Hero();
h2.hp = 400;
Hero h3= new Hero();
h3.hp = 300;
System.out.println(h1.equals(h2));
System.out.println(h1.equals(h3));
}
}
④hashCode()
hashCode方法返回一个对象的哈希值。
⑤线程同步相关方法
wait();
notify();
notifyAll();
⑥getClass()
getClass()会返回一个对象的类对象.
(四)、默认方法
①什么是默认方法
默认方法是JDK8新特性,指的是接口也可以提供具体方法了,而不像以前,只能提供抽象方法。。Mortal 这个接口,增加了一个默认方法 revive,这个方法有实现体,并且被声明为了default。
package charactor;
public interface Mortal {
public void die();
default public void revive() {
System.out.println("本英雄复活了");
}
}
②为什么会有默认方法
假设没有默认方法这种机制,那么如果要为Mortal增加一个新的方法revive,那么所有实现了Mortal接口的类,都需要做改动。
但是引入了默认方法后,原来的类,不需要做任何改动,并且还能得到这个默认方法
通过这种手段,就能够很好的扩展新的类,并且做到不影响原来的类。
六、多态
(一)、操作符的多态
同一个操作符在不同情境下,具备不同的作用
如果+号两侧都是整型,那么+代表 数字相加
如果+号两侧,任意一个是字符串,那么+代表字符串连接
package charactor;
public class Hero {
public String name;
protected float hp;
public static void main(String[] args) {
int i = 5;
int j = 6;
int k = i+j; //如果+号两侧都是整型,那么+代表 数字相加
System.out.println(k);
int a = 5;
String b = "5";
String c = a+b; //如果+号两侧,任意一个是字符串,那么+代表字符串连接
System.out.println(c);
}
}
(二)、类的多态
要实现类的多态,需要如下条件
- 父类(接口)引用指向子类对象
- 调用的方法有重写
class Animals{
private int age = 40;
public void method(){
System.out.println("animals");
}
}
class Cat extends Animals{
private int age = 20;
private String name = "ko";
public void method(){
System.out.println("cat");
}
public void play(){
System.out.println("play");
}
}
public class Text{
public static void main(String[] args){
Animals a = new Cat();
}
}
//a中有age且age = 40;a中没有name;
//a的method()方法为子类中重写后的方法,输出结果为"cat";a中没有play()方法
- 成员变量:编译看左边,执行看左边。
- 成员方法:编译看左边,执行看右边。
(三)、多态中的转型
向下转型:
Animals c = new Cat();
向上转型
Animals c = new Cat();
Cat a = (Cat)c;
七、日期
时间原点:1970年1月1日。
(一)、创建日期对象
package date;
//
import java.util.Date;
public class TestDate {
public static void main(String[] args) {
// 当前时间
Date d1 = new Date();
System.out.println("当前时间:");
System.out.println(d1);
System.out.println();
// 从1970年1月1日 早上8点0分0秒 开始经历的毫秒数
Date d2 = new Date(5000);
System.out.println("从1970年1月1日 早上8点0分0秒 开始经历了5秒的时间");
System.out.println(d2);
}
}
(二)、getTime()
getTime() 得到一个long型的整数。这个整数代表 从1970.1.1 08:00:00:000 开始 每经历一毫秒,增加1。直接打印对象,会看到 “Tue Jan 05 09:51:48 CST 2016” 这样的格式,可读性比较差。
package date;
//
import java.util.Date;
public class TestDate {
public static void main(String[] args) {
//注意:是java.util.Date;
//而非 java.sql.Date,此类是给数据库访问的时候使用的
Date now= new Date();
//打印当前时间
System.out.println("当前时间:"+now.toString());
//getTime() 得到一个long型的整数
//这个整数代表 1970.1.1 08:00:00:000,每经历一毫秒,增加1
System.out.println("当前时间getTime()返回的值是:"+now.getTime());
Date zero = new Date(0);
System.out.println("用0作为构造方法,得到的日期是:"+zero);
}
}
(三)、System.currentTimeMilis()
当前日期的毫秒数。
new Date().getTime() 和 System.currentTimeMillis() 是一样的,不过由于机器性能的原因,可能会相差几十毫秒,毕竟每执行一行代码,都是需要时间的。
package date;
//
import java.util.Date;
public class TestDate {
public static void main(String[] args) {
Date now= new Date();
//当前日期的毫秒数
System.out.println("Date.getTime() \t\t\t返回值: "+now.getTime());
//通过System.currentTimeMillis()获取当前日期的毫秒数
System.out.println("System.currentTimeMillis() \t返回值: "+System.currentTimeMillis());
}
}
(四)、日期格式化
SimpleDateFormat 日期格式化类
①日期转字符串
package date;
//
import java.text.SimpleDateFormat;
import java.util.Date;
public class TestDate {
public static void main(String[] args) {
//y 代表年
//M 代表月
//d 代表日
//H 代表24进制的小时
//h 代表12进制的小时
//m 代表分钟
//s 代表秒
//S 代表毫秒
SimpleDateFormat sdf =new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS" );
Date d= new Date();
String str = sdf.format(d);
System.out.println("当前时间通过 yyyy-MM-dd HH:mm:ss SSS 格式化后的输出: "+str);
SimpleDateFormat sdf1 =new SimpleDateFormat("yyyy-MM-dd" );
Date d1= new Date();
String str1 = sdf1.format(d1);
System.out.println("当前时间通过 yyyy-MM-dd 格式化后的输出: "+str1);
}
}
②字符串转日期
模式(yyyy/MM/dd HH:mm:ss)需要和字符串格式保持一致,如果不一样就会抛出解析异常ParseException。
package date;
//
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class TestDate {
public static void main(String[] args) {
SimpleDateFormat sdf =new SimpleDateFormat("yyyy/MM/dd HH:mm:ss" );
String str = "2016/1/5 12:12:12";
try {
Date d = sdf.parse(str);
System.out.printf("字符串 %s 通过格式 yyyy/MM/dd HH:mm:ss %n转换为日期对象: %s",str,d.toString());
} catch (ParseException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
(五)、Calendar类
①Calendar与Date进行转换
采用单例模式获取日历对象Calendar.getInstance();
package date;
//
import java.util.Calendar;
import java.util.Date;
public class TestDate {
public static void main(String[] args) {
//采用单例模式获取日历对象Calendar.getInstance();
Calendar c = Calendar.getInstance();
//通过日历对象得到日期对象
Date d = c.getTime();
Date d2 = new Date(0);
c.setTime(d2); //把这个日历,调成日期 : 1970.1.1 08:00:00
}
}
②翻日历
add方法,在原日期上增加年/月/日。
set方法,直接设置年/月/日。
package date;
import java.text.SimpleDateFormat;
//
import java.util.Calendar;
import java.util.Date;
public class TestDate {
private static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
public static void main(String[] args) {
Calendar c = Calendar.getInstance();
Date now = c.getTime();
// 当前日期
System.out.println("当前日期:\t" + format(c.getTime()));
// 下个月的今天
c.setTime(now);
c.add(Calendar.MONTH, 1);
System.out.println("下个月的今天:\t" +format(c.getTime()));
// 去年的今天
c.setTime(now);
c.add(Calendar.YEAR, -1);
System.out.println("去年的今天:\t" +format(c.getTime()));
// 上个月的第三天
c.setTime(now);
c.add(Calendar.MONTH, -1);
c.set(Calendar.DATE, 3);
System.out.println("上个月的第三天:\t" +format(c.getTime()));
}
private static String format(Date time) {
return sdf.format(time);
}
}
八、数字与字符串
(一)、装箱和拆箱
①封装类
所有的基本类型,都有对应的类类型。比如int对应的类是Integer,这种类就叫做封装类。
- byte Byte
- short Short
- long Long
- int Integer
- float Float
- double Double
- char Character
- boolean Boolean
②Number类
数字封装类有,Byte,Short,Integer,Long,Float,Double。这些类都是抽象类Number的子类。
package digit;
public class TestNumber {
public static void main(String[] args) {
int i = 5;
Integer it = new Integer(i);
//Integer是Number的子类,所以打印true
System.out.println(it instanceof Number);
}
}
③基本类型转封装类
package digit;
public class TestNumber {
public static void main(String[] args) {
int i = 5;
//基本类型转换成封装类型
Integer it = new Integer(i);
}
}
④封装类转基本类型
package digit;
public class TestNumber {
public static void main(String[] args) {
int i = 5;
//基本类型转换成封装类型
Integer it = new Integer(i);
//封装类型转换成基本类型
int i2 = it.intValue();
}
}
⑤自动装箱
不需要调用构造方法,通过=符号自动把 基本类型 转换为 类类型 就叫装箱
package digit;
public class TestNumber {
public static void main(String[] args) {
int i = 5;
//基本类型转换成封装类型
Integer it = new Integer(i);
//自动转换就叫装箱
Integer it2 = i;
}
}
⑥自动拆箱
不需要调用Integer的intValue方法,通过=就自动转换成int类型,就叫拆箱。
package digit;
public class TestNumber {
public static void main(String[] args) {
int i = 5;
Integer it = new Integer(i);
//封装类型转换成基本类型
int i2 = it.intValue();
//自动转换就叫拆箱
int i3 = it;
}
}
⑦int的最大值,最小值
int的最大值可以通过其对应的封装类Integer.MAX_VALUE获取。
package digit;
public class TestNumber {
public static void main(String[] args) {
//int的最大值
System.out.println(Integer.MAX_VALUE);
//int的最小值
System.out.println(Integer.MIN_VALUE);
}
}
(二)、字符串转换
①数字转字符串
方法1: 使用String类的静态方法valueOf。
方法2: 先把基本类型装箱为对象,然后调用对象的toString。
package digit;
public class TestNumber {
public static void main(String[] args) {
int i = 5;
//方法1
String str = String.valueOf(i);
//方法2
Integer it = i;
String str2 = it.toString();
}
}
②字符串转数字
调用Integer的静态方法parseInt
package digit;
public class TestNumber {
public static void main(String[] args) {
String str = "999";
int i= Integer.parseInt(str);
System.out.println(i);
}
}
(三)、数学方法
java.lang.Math提供了一些常用的数学运算方法,并且都是以静态方法的形式存在
①四舍五入, 随机数,开方,次方,π,自然常数
package digit;
public class TestNumber {
public static void main(String[] args) {
float f1 = 5.4f;
float f2 = 5.5f;
//5.4四舍五入即5
System.out.println(Math.round(f1));
//5.5四舍五入即6
System.out.println(Math.round(f2));
Math.ceil(f1);//向上取整
Math.floor(f2);//向下取整
//得到一个0-1之间的随机浮点数(取不到1)
System.out.println(Math.random());
//得到一个0-10之间的随机整数 (取不到10)
System.out.println((int)( Math.random()*10));
//开方
System.out.println(Math.sqrt(9));
//次方(2的4次方)
System.out.println(Math.pow(2,4));
Math.abs(f2);//取绝对值
//π
System.out.println(Math.PI);
//自然常数
System.out.println(Math.E);
}
}
(四)、格式化输出
①格式化输出
%s 表示字符串。
%d 表示数字。
%n 表示换行。
换行符就是另起一行 — ‘\n’ 换行(newline)。
回车符就是回到一行的开头 — ‘\r’ 回车(return)。
在eclipse里敲一个回车,实际上是回车换行符。
Java是跨平台的编程语言,同样的代码,可以在不同的平台使用,比如Windows,Linux,Mac。
然而在不同的操作系统,换行符是不一样的:
(1)在DOS和Windows中,每行结尾是 “\r\n”;
(2)Linux系统里,每行结尾只有 “\n”;
(3)Mac系统里,每行结尾是只有 “\r”。
为了使得同一个java程序的换行符在所有的操作系统中都有一样的表现,使用%n,就可以做到平台无关的换行。
②printf和format
printf和format能够达到一模一样的效果,通过eclipse查看java源代码 可以看到,在printf中直接调用了format。
③总长度,左对齐,补0,千位分隔符,小数点位数,本地化表达
package digit;
import java.util.Locale;
public class TestNumber {
public static void main(String[] args) {
int year = 2020;
//总长度,左对齐,补0,千位分隔符,小数点位数,本地化表达
//直接打印数字
System.out.format("%d%n",year);
//总长度是8,默认右对齐
System.out.format("%8d%n",year);
//总长度是8,左对齐
System.out.format("%-8d%n",year);
//总长度是8,不够补0
System.out.format("%08d%n",year);
//千位分隔符
System.out.format("%,8d%n",year*10000);
//小数点位数
System.out.format("%.2f%n",Math.PI);
//不同国家的千位分隔符
System.out.format(Locale.FRANCE,"%,.2f%n",Math.PI*10000);
System.out.format(Locale.US,"%,.2f%n",Math.PI*10000);
System.out.format(Locale.UK,"%,.2f%n",Math.PI*10000);
}
}
(五)、字符
①char对应的封装类
char对应的封装类是Character
package character;
public class TestChar {
public static void main(String[] args) {
char c1 = 'a';
Character c = c1; //自动装箱
c1 = c;//自动拆箱
}
}
②Character常见方法
package character;
public class TestChar {
public static void main(String[] args) {
System.out.println(Character.isLetter('a'));//判断是否为字母
System.out.println(Character.isDigit('a')); //判断是否为数字
System.out.println(Character.isWhitespace(' ')); //是否是空白
System.out.println(Character.isUpperCase('a')); //是否是大写
System.out.println(Character.isLowerCase('a')); //是否是小写
System.out.println(Character.toUpperCase('a')); //转换为大写
System.out.println(Character.toLowerCase('A')); //转换为小写
String a = 'a'; //不能够直接把一个字符转换成字符串
String a2 = Character.toString('a'); //转换为字符串
}
}
(六)、字符串
- 特点
- 字符串中的内容不能被改变
- 字符串可以被共享使用
- 字符串效果上相当于char[]数组,底层原理上是byte[]数组
- 3+1种创建方式
构造方法
public String(); public String(char[] char);
例:
char[] charArray = {'a','b','c'}; String str = new String(charArray);
构造方法
public String(byte[] byte);
例:
byte[] byteArray = {1,2,3} String str = new String(byteArray);
- 当输出对象时,默认调用toString()方法,会直接输出对象的内存地址
- 若要输出对象中的各成员内容,需要重写toString()方法
①创建字符串
字符串即字符的组合,在Java中,字符串是一个类,所以我们见到的字符串都是对象
常见创建字符串手段:
- 每当有一个字面值出现的时候,虚拟机就会创建一个字符串;
- 调用String的构造方法创建一个字符串对象;
- 通过+加号进行字符串拼接也会创建新的字符串对象。
②final
String 被修饰为final,所以是不能被继承的。
package character;
public class TestString {
public static void main(String[] args) {
MyString str = new MyString();
}
/*这里会报错,因为String不能被继承*/
static class MyString extends String{
}
}
③immutable
immutable 是指不可改变的。比如创建了一个字符串对象:
String garen =“盖伦”;
不可改变的具体含义是指:
不能增加长度
不能减少长度
不能插入字符
不能删除字符
不能修改字符
一旦创建好这个字符串,里面的内容 永远 不能改变,String 的表现就像是一个常量。
④字符串格式化
如果不使用字符串格式化,就需要进行字符串连接,如果变量比较多,拼接就会显得繁琐,使用字符串格式化,就可以简洁明了。
package character;
public class TestString {
public static void main(String[] args) {
String name ="盖伦";
int kill = 8;
String title="超神";
//直接使用+进行字符串连接,编码感觉会比较繁琐,并且维护性差,易读性差
String sentence = name+ " 在进行了连续 " + kill + " 次击杀后,获得了 " + title +" 的称号";
System.out.println(sentence);
//格式化字符串
//%s表示字符串,%d表示数字,%n表示换行
String sentenceFormat ="%s 在进行了连续 %d 次击杀后,获得了 %s 的称号%n";
String sentence2 = String.format(sentenceFormat, name,kill,title);
System.out.println(sentence2);
}
}
⑤字符串长度
length方法返回当前字符串的长度,可以有长度为0的字符串,即空字符串。
package character;
public class TestString {
public static void main(String[] args) {
String name ="盖伦";
System.out.println(name.length());
String unknowHero = "";
//可以有长度为0的字符串,即空字符串
System.out.println(unknowHero.length());
}
}
(七)、操纵字符串
①获取字符
charAt(int index)获取指定位置的字符。
package character;
public class TestString {
public static void main(String[] args) {
String sentence = "盖伦,在进行了连续8次击杀后,获得了 超神 的称号";
char c = sentence.charAt(0);
System.out.println(c);
}
}
②获取对应的字符数组
toCharArray(),获取对应的字符数组。
package character;
public class TestString {
public static void main(String[] args) {
String sentence = "盖伦,在进行了连续8次击杀后,获得了超神 的称号";
char[] cs = sentence.toCharArray(); //获取对应的字符数组
System.out.println(sentence.length() == cs.length);
}
}
③截取子字符串
subString,截取子字符串。
package character;
public class TestString {
public static void main(String[] args) {
String sentence = "盖伦,在进行了连续8次击杀后,获得了 超神 的称号";
//截取从第3个开始的字符串 (基0)
String subString1 = sentence.substring(3);
System.out.println(subString1);
//截取从第3个开始的字符串 (基0)
//到5-1的位置的字符串
//左闭右开
String subString2 = sentence.substring(3,5);
System.out.println(subString2);
}
}
④分隔
split,根据分隔符进行分隔。
package character;
public class TestString {
public static void main(String[] args) {
String sentence = "盖伦,在进行了连续8次击杀后,获得了 超神 的称号";
//根据,进行分割,得到3个子字符串
String subSentences[] = sentence.split(",");
for (String sub : subSentences) {
System.out.println(sub);
}
}
}
⑤去掉首尾空格
trim,去掉首尾空格。
package character;
public class TestString {
public static void main(String[] args) {
String sentence = " 盖伦,在进行了连续8次击杀后,获得了 超神 的称号 ";
System.out.println(sentence);
//去掉首尾空格
System.out.println(sentence.trim());
}
}
⑥大小写
toLowerCase 全部变成小写。
toUpperCase 全部变成大写。
package character;
public class TestString {
public static void main(String[] args) {
String sentence = "Garen";
//全部变成小写
System.out.println(sentence.toLowerCase());
//全部变成大写
System.out.println(sentence.toUpperCase());
}
}
⑦定位
indexOf 判断字符或者子字符串出现的位置。
contains 是否包含子字符串。
package character;
public class TestString {
public static void main(String[] args) {
String sentence = "盖伦,在进行了连续8次击杀后,获得了超神 的称号";
System.out.println(sentence.indexOf('8')); //字符第一次出现的位置
System.out.println(sentence.indexOf("超神")); //字符串第一次出现的位置
System.out.println(sentence.lastIndexOf("了")); //字符串最后出现的位置
System.out.println(sentence.indexOf(',',5)); //从位置5开始,出现的第一次,的位置
System.out.println(sentence.contains("击杀")); //是否包含字符串"击杀"
}
}
⑧替换
replaceAll 替换所有的。
replaceFirst 只替换第一个。
package character;
public class TestString {
public static void main(String[] args) {
String sentence = "盖伦,在进行了连续8次击杀后,获得了超神 的称号";
String temp = sentence.replaceAll("击杀", "被击杀"); //替换所有的
temp = temp.replaceAll("超神", "超鬼");
System.out.println(temp);
temp = sentence.replaceFirst(",","");//只替换第一个
System.out.println(temp);
}
}
(八)、比较字符串
①是否是同一个对象
str1和str2的内容一定是一样的!但是,并不是同一个字符串对象。
package character;
public class TestString {
public static void main(String[] args) {
String str1 = "the light";
String str2 = new String(str1);
//==用于判断是否是同一个字符串对象
System.out.println( str1 == str2);
}
}
特例
一般说来,编译器每碰到一个字符串的字面值,就会创建一个新的对象
所以在第6行会创建了一个新的字符串"the light"。但是在第7行,编译器发现已经存在现成的"the light",那么就直接拿来使用,而没有进行重复创建。
package character;
public class TestString {
public static void main(String[] args) {
String str1 = "the light";
String str3 = "the light";
System.out.println( str1 == str3);
}
}
②内容是否相同
使用equals进行字符串内容的比较,必须大小写一致。
equalsIgnoreCase,忽略大小写判断内容是否一致。
package character;
public class TestString {
public static void main(String[] args) {
String str1 = "the light";
String str2 = new String(str1);
String str3 = str1.toUpperCase();
//==用于判断是否是同一个字符串对象
System.out.println( str1 == str2);
System.out.println(str1.equals(str2));//完全一样返回true
System.out.println(str1.equals(str3));//大小写不一样,返回false
System.out.println(str1.equalsIgnoreCase(str3));//忽略大小写的比较,返回true
}
}
③是否是以字符串开始或者结束
startsWith //以…开始。
endsWith //以…结束。
package character;
public class TestString {
public static void main(String[] args) {
String str1 = "the light";
String start = "the";
String end = "Ight";
System.out.println(str1.startsWith(start));//以...开始
System.out.println(str1.endsWith(end));//以...结束
}
}
(九)、int 和 String类型相互转换
①int 转 String
int num = 100;
①方式一
String s = "" + num;
②方式二
String s = String.valueOf(num);
②String 转 int
String s = "100";
①方式一
Integer i = Integer.valueOf(s); int x = i.intValue();
②方式二
int x = Integer.parseInt(s);
(十)、StringBuffer和StringBuilder
可修改的字符串
①追加 删除 插入 反转
append追加
delete 删除
insert 插入
reverse 反转
package character;
public class TestString {
public static void main(String[] args) {
String str1 = "let there ";
StringBuffer sb = new StringBuffer(str1); //根据str1创建一个StringBuffer对象
sb.append("be light"); //在最后追加
System.out.println(sb);
sb.delete(4, 10);//删除4-10之间的字符
System.out.println(sb);
sb.insert(4, "there ");//在4这个位置插入 there
System.out.println(sb);
sb.reverse(); //反转
System.out.println(sb);
}
}
②长度 容量
为什么StringBuffer可以变长?
和String内部是一个字符数组一样,StringBuffer也维护了一个字符数组。 但是,这个字符数组,留有冗余长度。
比如说new StringBuffer(“the”),其内部的字符数组的长度,是19,而不是3,这样调用插入和追加,在现成的数组的基础上就可以完成了。
如果追加的长度超过了19,就会分配一个新的数组,长度比原来多一些,把原来的数据复制到新的数组中,看上去 数组长度就变长了 。
注: 19这个数量,不同的JDK数量是不一样的
package character;
public class TestString {
public static void main(String[] args) {
String str1 = "the";
StringBuffer sb = new StringBuffer(str1);
System.out.println(sb.length()); //内容长度
System.out.println(sb.capacity());//总空间
}
}
③StringBuilder:
与StringBuffer方法和使用相同,区别在于StringBuilder线程不安全但性能略高。