目录
相关文章
一、Java中的变量和常量
java是强类型语言。
每一个变量都要明确的声明它的类型。
1. 变量
变量名规则
- 由字母或字母与数字组成;
- 字母有大小写区分
变量初始化
- 指第一次赋值
int a;//声明变量
a = 10;//变量赋值
也可以将变量的声明和初始化写在一起:
int a = 10;
变量分局部变量和全局变量
- 局部变量:定义在方法中的变量,该变量只能在该方法中使用,没有默认值,变量名不能重名
public static void change(){ int a = 10;//变量a定义在了change()方法中,只能在change()方法中使用 System.out.println(a); }
- 全局变量:定义在类下,该变量可以在所以方法中使用,有默认值
- int默认值为0
- 浮点数默认值为0.0
- char类型默认值为空字符
- 布尔类型默认值为false
- 引用类型默认值为空
public class Text{ int a = 10;//变量a定义在了Text类下 public static void change(){ System.out.println(a);//能在change()方法中访问到变量a } }
- 两种变量均存在,且局部变量和全局变量变量名重复了,局部变量优先
public class Text{ int a = 10; public static void change(){ int a = 100; System.out.println(a);//输出:100 } }
2. 常量
- 常量声明的时候必须要赋值
- 只能赋值一次,指除了第一次赋值外,后续不能在赋值,不再变的量
- 使用
final
修饰 - 常量名使用全大写,例如:
static final double PI = 3.14;
二、final(重点)
- 修饰的基本类型不可以第二次赋值;
- 修饰的引用类型不可以第二次改变指向;
- 修饰的类不可以被继承;
- 修饰的方法不可以被重写;
- 防止指令重排序,遏制流水线优化,可以保证多线程并发场景下的可见性;
- 通常与
static
一起用。
三、运算
1. 基本运算符
- 加+、减-、乘*、除/、取余%
- 注意:
- 除法中,两个数都是整数,商只保留整数部分;左右两边至少有一个带小数,商为浮点数
System.out.println(5/2);//输出:2 System.out.println(5.0/2);//输出:2.5 System.out.println(5/2.0);//输出:2.5
- 整数/0,报错;浮点数/0,报Infinity(无穷大的意思)
System.out.println(8/0);//输出:报错 System.out.println(8.0/0);//输出:Infinity
- 除法中,两个数都是整数,商只保留整数部分;左右两边至少有一个带小数,商为浮点数
数学函数
Math类
:
- 平方根:
sqrt()
- 平方:
pow( , )
- 默认使用double类型存储
System.out.println(Math.sqrt(21));//计算21的平方根 System.out.println(Math.pow(21,2);//计算21的平方
- 默认使用double类型存储
StrictMath 类
:
- 与
Math类
相同的方法double result = StrictMath.sqrt(2); // 严格数学运算
BigDecimal 类
:
- 用于高精度十进制运算
- 解决浮点数精度问题
- 提供精确的加减乘除运算
BigDecimal a = new BigDecimal("0.1"); BigDecimal b = new BigDecimal("0.2"); BigDecimal sum = a.add(b); // 精确得到0.3
BigInteger 类
:
- 处理任意精度整数运算
- 适用于超出
long
范围的整数计算 valueOf()方法
:是一个静态工厂方法,将基本数据类型的long
值转换为 BigInteger 对象public static BigInteger valueOf(long val)
multiply()方法
:用于计算两个 BigInteger 的乘积,返回一个新的 BigInteger 对象public BigInteger multiply(BigInteger val)
BigInteger big1 = new BigInteger("12345678901234567890"); BigInteger big2 = BigInteger.valueOf(987654321L); BigInteger product = big1.multiply(big2);
2. 自增和自减运算符
++
运算:
- 单独放在一行没区别
int a = 10; a++;//输出:10 ++a;//输出:10
- 直接赋值给变量,有区别
- 放在元素前:先运算+1,在赋值
int a = 10; int b = ++a;//输出:11 //先运算:a = a + 1 = 11 //在赋值:b = a = 11
- 放在运算后:先赋值,在运算+1
int a = 10; int b = a++;//输出:10 //先赋值:b = a = 10
- 放在元素前:先运算+1,在赋值
- 在一次操作中,如果有两个等号对一个变量进行赋值,只认第一个等号
int a = 10; a = a++;//输出:10 先赋值:a=a 在运算:a=a+1 a = ++a;//输出:11 先运算:a=a+1 在赋值:a=a //先赋值:b = a = 10
--
运算:与++
运算同理。
3. 结合赋值和运算符
二元运算符:+=、-=、*=、/=
+=
运算:
int a = 10;
a = a + 20;
//等价于
a += 20;
关系运算符和逻辑运算符经常用在条件判断中。
4. 关系运算符
- 大于>
- 小于<
- 大于等于>=
- 小于等于<=
- 不等于!=
- 等于=
示例:
int a = 10;
int b = 20;
if(a>b){
System.out.println("a");
}else{
System.out.println("b");
}//输出:b
5. 逻辑运算符
- 逻辑与&&:两个条件都要为真,结果才为真
- 逻辑或||:两个条件中只要有一个为真,结果就为真
- 逻辑与和逻辑或为短路逻辑与和短路逻辑或,逻辑与中,第一个条件为假,后面的条件就不再执行了
- 一般用在引用类型的数据、链表,避免一些有风险的代码
示例1:
int a = 10;
int b = 20;
int c = 30;
if(a<b&&a>c){
System.out.println("a");
}else{
System.out.println("b");
}//输出:b
示例2:
String str = null;
if(str!=null&&str.length()>10){
System.out.println("a");
}else{
System.out.println("b");
}//输出:b
6. 位运算
针对于二进制数据的运算,都是补码运算。
- 按位与&(and):两位数运算,上下都为1,结果为1;上下有0,结果为0
00011010 00011111 ------------ 00011010
- 按位或|(or):两位数运算,上下都为0,结果为0;上下有1,结果为1
00011010 00011111 ------------ 00011111
- 按位异或^(xor):两位数运算,上下不同,结果为1;上下相同,结果为0
00011010 00011111 ------------ 00000101
- 按位取反~(not):针对一位数
00011010 ------------ 11100101
应用题1:给一个二进制的数0101x001,想知道第四位给的是什么数?
解决方案:与运算,任何数与1进行与运算,得到的都是任何数它本身
让0101x001二进制与00001000进行与运算,上下都为1,结果为1;上下有0,结果为0
如果结果为0,x为0;如果结果为1,x为1
0101x001
00001000
------------
0000 000
应用题2:不借第三方变量,如何实现两个数据交换?
解决方案:异或运算
int a = 5;//二进制:0000 0101
int b = 3;//二进制:0000 0011
a = a^b; // a = 0000 0110(上面两个进行异或运算)
b = a^b; // b = 0000 0101(第二个与a进行异或运算)
a = a^b; // a = 0000 0011(a和b进行异或运算)
System.out.println(a);//输出:3
System.out.println(b);//输出:5
问题:为什么有位运算?
与CPU底层相关联。
CPU底层用的都是半导体电路,设计了下面几种半导体电路:
7. 移位运算
移几位,补几位的0,超过位数,删除多余的0
- 左移<<(变大):往左移,右边补0,左边删0
示例1: 0000 0010 往左移3位 0000 0010 000 右边补3个0,超出8位 0001 0000 删除左边的三个0 示例2: 4*8=4*2^3 往左移动三位 0000 0100 4的二进制 0000 0100 000 右边补三个0 0010 0000 超出8位,左边删除3个0 示例3: 4*9=4*(8+1)=4*2^3+4*2^0 0000 0100 4的二进制 4*2^3 --> 左移3位,右边补3个0,左边删3个0 --> 0010 0000 4*2^0 --> 不移动 --> 0000 0100 相加: 0010 0000 0000 0100 ------------ 0010 0100 --> 32+4=36
- 右移>>(变小):往右移,左边补0,右边删0
System.out.println(8>>2);//输出:2
- 有符号右移>>:正数,左边补0;复数,左边补1
示例1:负数(使用补码运算,源码转为反码,反码+1==补码) System.out.println(-8>>2);//输出:-2 -8 --> 源码:1000 1000 --> 反码:11110111 --> 反码+1:1111 1000 --> 补码:1111 1000 1111 1000 往右移2位 11 1111 1000 左边补2位1 1111 1110 超出8位,右边删2个0 补码:1111 1110 --> 补码-1:1111 1101 --> 反码:1000 0010 --> 源码:1000 0010 示例2:正数(源码==补码) System.out.println(8>>2);//输出:2 8 --> 源码:0000 1000 -->补码:0000 1000 0000 1000 往右移2位 00 0000 1000 左边补2位0 0000 0010 超出8位,右边删2个0 补码:0000 0010 --> 源码:0000 0010
- 无符号右移>>>:无论是正数还是负数,左边均补0
System.out.println(-8>>>2);//输出:1073741822 上面举例使用的都是byte类型的数据,实际上默认使用的是int类型数据 10000000 00000000 00000000 00001000 -8的int类型的源码 11111111 11111111 11111111 11110111 -8的反码 11111111 11111111 11111111 11111000 -8的的补码 00 11111111 11111111 11111111 11111000 左边补2个0 00111111 11111111 11111111 11111110 超出32位,右边删2个0 00111111 11111111 11111111 11111110 正数的源码==补码 --> 1073741822
- 有符号右移>>:正数,左边补0;复数,左边补1
四、字符串
- 是引用类型中的特殊情况
1. 定义方式:
- 方式一:直接赋值
String a = "demo";//底层就是一个数组
- 方式二:面向对象的赋值
String b = new String("demo");
- 两者的区别:
- 判断两个字符串是否相等,就是判断是否指向同一空间,从下面的内存图中可以看出:a和b指向的是同一空间,a和c指向的空间不同,c和d指向的空间也不同
String a = "demo"; String b = "demo"; String c = new String("demo"); String d = new String("demo"); System.out.println(a==b);//输出:true System.out.println(a==c);//输出:false System.out.println(c==d);//输出:false
2. 字符串可使用的方法
equals()
方法:检测字符串是否相等String a = new String("demo"); String b = new String("demo"); System.out.println(a.equals(b));//输出:true
- 替换:
replace(old,new)
String a = new String("demo"); System.out.println(a.replace("o","x"));//输出:demx
- 拆分:
split()
String a = new String("demo"); String[] arr = b.split("e")//根据e进行拆分 System.out.println(Arrays.toString(arr));//输出:[d,mo]
- 字符串的查询与比较
- 获取字符串长度:
length()
String a = "demo"; int len = a.length();//输出:4
- 获取指定位置字符:
charAt()
String a = "demo"; char ch = a.charAt(1);//输出:'d'
- 查找字符位置:
indexOf()
String a = "demo"; int pos1 = str.indexOf('e'); // 1 int pos2 = str.indexOf("em"); // 1 int pos3 = str.indexOf('m', 2); // 从索引2开始找 → 2
- 从后向前查找:
lastIndexOd()
String a = "demo"; int lastPos = str.lastIndexOf('e');//输出:1
- 字典序比较:
compareTo(String)
int diff = "apple".compareTo("banana"); // 负数(a<b)
- 是否包含子串:
contains()
String a = "demo"; boolean hasEll = str.contains("emo"); // true
- 获取字符串长度:
- 字符串操作
- 截取子串:
substring()
String a = "demo"; String sub1 = str.substring(1); //输出:"emo" String sub2 = str.substring(1,3); //输出:"em" (含头不含尾)
- 连接字符串:
concat(String)
String a = "Hello"; String newStr = str.concat(" World"); //输出:"Hello World"
- 大小写转换:
toLowerCase()/toUpperCase()
String a = "demo"; String upper = str.toUpperCase(); //输出:"DEMO"
- 取出首位空白:
trim()
String a = " demo ".trim(); //输出:"demo"
- 增强版取出首位空白:
strip()
String a = "demo"; String stripped = "\u2000demo\u2000".strip(); //输出:"demo"
- 截取子串:
- 字符串检查
- 是否空字符串:
isEmpty()
boolean empty = "".isEmpty(); // true
- 是否空白字符串:
isBlank()
(java 11+)boolean blank = " ".isBlank(); // true
- 开头/结尾检查:
startsWith()/endWith()
String a = "demo"; boolean starts = str.startsWith("d"); // true boolean ends = str.endsWith("mo"); // true
- 是否空字符串:
- 字符串转换
- 转为字符数组:
toCharArray()
String a = "demo"; char[] chars = str.toCharArray(); // ['d','e','m','o']
- 转为字节数组:
getBytes()
byte[] bytes = str.getBytes(); // 默认编码 byte[] utf8 = str.getBytes(StandardCharsets.UTF_8);
- 格式化字符串:
format()
String formatted = String.format("Name: %s, Age: %d", "Alice", 25);
- 转为字符数组:
- 字符串拼接:
+
或join()
String joined = String.join("-", "A","B","C"); // "A-B-C"
- 分隔为行流:
lines()
long lineCount = "Line1\nLine2".lines().count(); // 2
- 重复字符串:
repeat(int)
String repeated = "Ab".repeat(3); // "AbAbAb"
3. 不可变字符串
不可变字符串:不能在源地址直接修改
String a = "demo";
String b = new String("demo");
原因:底层是数组,数组在声明空间的时候,是要提前说明空间的大小
但是,可以拼接
String a = "demo";
String b = "123";
String c = a + b;
String d = "demo123";
System.out.println(c);//输出:"demo123"
System.out.println(c==d);//输出:false 指向的不是同一空间
编译:javac 文件名
--> 生成编译文件
执行:java 编译文件名
反编译:javap -c 编译文件名
String a = "demo"+"123";//a指向demo的空间,123的空间没有被指向,被优化,与demo合并,因此a指向的是demo123的空间
String b = "demo123";
System.out.println(a==b);//输出:true 指向的是同一空间
4. 可变字符串
StringBuffer
:线程安全(可作用于多线程)- 有加锁解锁的过程,加锁,等修改完,才能访问,运行稍慢一些
StringBuilder
:线程不安全 (可作用于单线程)StringBuilder a = new StringBuilder("demo"); System.out.println(a);//输出:'demo'
- 追加:
append()
方法StringBuilder a = new StringBuilder("demo"); StringBuilder b = a.append("123"); System.out.println(a);//输出:'demo123' 在源地址上追加 System.out.println(a==b);//输出:true
- 插入内容:
insert()
StringBuilder a = new StringBuilder("demo"); sb.insert(4, " test"); // "demo test"
- 删除子序列:
delete(int start,int end)
StringBuilder a = new StringBuilder("demo"); sb.delete(2, 3); // 删除索引2-3的内容,输出:"de"
- 删除指定字符
deleteCharAt()
StringBuilder a = new StringBuilder("demo"); sb.deleteCharAt(0); // 删除第一个字符,输出:"emo"
- 替换内容:
replace(int start, int end, String str)
StringBuilder a = new StringBuilder("demo"); sb.replace(0, 2, "Hi"); // 替换索引0-2的内容,输出:"Himo"
- 反转字符串:
reverse()
StringBuilder a = new StringBuilder("demo"); sb.reverse(); // 反转字符串内容,输出:"omed"
扩展:
时间戳:currentTimeMillis()
long start = System.currentTimeMillis();
String a = "";
for(int i=0;i<10000;i++){
a = a + "1";
}
long end= System.currentTimeMillis();
System.out.println(end-start);//输出:4860
long start1 = System.currentTimeMillis();
StringBuffer a1 = new StringBuffer("");
for(int i=0;i<10000;i++){
a1.append("1");
}
long end1= System.currentTimeMillis();
System.out.println(end1-start1);//输出:1
将数据进行分页,每页都有一个地址,传输每页的地址,可读取每页中的内容。
上述代码中
- String处代码,每次+1,都要在堆区开辟一个新空间,并回收旧空间;加10000个1,就需要开辟10000次新空间,回收10000次旧空间。
- StringBuffer底层是数组,可以扩容,在一个内存页中不断扩容追加,不用开辟新空间和销毁旧空间的过程。
- 因此,StringBuffer比String速度块。