1.变量与数据类型
1.1 标识符
java对包,类,方法,参数和变量等要素命名时使用的字符序列称为标识符。
1.2 命名规则
- 由字母,数字,下划线(_)和美元符号($)组成
- 不能以数字开头。
- 区分大小。
- 长度无限制。
- 不能是Java中的保留关键字。
1.3命名规范
- 标识符命名习惯:见名知意。
- 类名规范:首字母大写,后面每个单词首字母大写(大驼峰式)。
- 方法名规范: 首字母小写,后面每个单词首字母大写(小驼峰式)。
- 变量名规范: 首字母小写,后面每个单词首字母大写。
2.关键字
Java中有一些赋予特定的含义,有专门
用途的字符串称为关键字(keyword)。全部是小写。
3.进制
3.1 二进制
- 计算机中的数据都以二进制数字保存。
- 二进制:逢二进一。即只有0、1两个值。
如:十进制的10在计算机内保存为二进制的1010 - 计算机中信息的存储单位
位(Bit):表示一个二进制数码0或1,是计算机存储处理信息的最基本的单位。
字节(Byte):一个字节由8个位组成。它表示作为一个完整处理单位的8个二进制数码。
字:32位计算机:1字=32位=4字节,64位计算机:1字=64位=8字节
3.2十六进制
二进制表示法太冗长,所以在程序中一般喜欢用十六进制。
十六进制:基数为十六,逢十六进一。它用abcdef表示从0-9之上的值。
Java中十六进制数据要以0x或0X开头。如:0x23D
十六进制转换成二进制只需将每个十六进制数字替换为相对应的四个二进制位即可。
二进制 十六进制
二进制 | 十六进制 |
---|---|
0000 | 0 |
0001 | 1 |
0010 | 2 |
0011 | 3 |
0100 | 4 |
0101 | 5 |
0110 | 6 |
0111 | 7 |
1000 | 8 |
1001 | 9 |
1010 | A |
1011 | B |
1100 | C |
1101 | D |
1110 | E |
1111 | F |
3.3 八进制
八进制:基数为八。
Java中八进制数据要以0开头。如:0123
八进制转换成二进制:只需将每个八进制数字替换为相对应的三个二进制位即可。
现在的计算机系统很少用八进制的了。
4.原码,反码,补码
4.1 原码
十进制数据的二进制表现形式就是原码,原码最左边的一个数字就是符号位,0为正,1为负。
4.2 反码
正数的反码是其本身(等于原码),负数的反码是符号位保持不变,其余位取反。 反码的存在是为了正
确计算负数,因为原码不能用于计算负数
4.3 补码
正数的补码是其本身,负数的补码等于其反码 +1。因为反码不能解决负数跨零(类似于 -6 + 7)的问
题,所以补码出现了。
总结:
- 原码:最高位符号位,0表示正数,1:表示负数
- 反码:正数的反码不变与原码相同,负数反码:符号位不变,其他按位取反,解决负数的计算问题
- 补码:正数的补码不变与原码相同,负数补码是反码+1,解决跨零计算问题
5.变量
5.1 定义
变量就是系统为程序分配的一块内存单元,用来存储各种类型的数据。简单理解就是内存中一块空间的
表示。
由于该存储单元中的数据可以发生改变,因此得名为"变量"。
5.2 变量的使用
package lina;
/*
* 变量的声明与使用
*/
public class Demo02_var {
public static void main(String[] args) {
//1、声明变量
//1.1 先声明后赋值:数据类型 变量名称;
int age;//声明
age=18;//赋值
//1.2 声明变量的同时赋初值:数据类型 变量名称=初值;
double money=1000.0;
//2、变量的使用:先声明并赋值之后才能使用
System.out.println("年龄是:"+age);
System.out.println("私房钱有:"+money);
//System.out.println(name);//错误 没有声明
//其他类型的变量
float num=12.5F;//小数点的数字默认是double类型,如果赋值给float,需要在值后面
加上后缀f 大小写都OK
long num2=100L;//整数数字默认是int类型,long类型的 变量的值后面可以添加L后
缀,也可以省略(省略的时候暗含类型转换,但是不能超过int的范围,否则不能省略)
char gender='男';//字符类型的值必须使用单引号引起来
String str="我爱你中国";//字符串类型的值必须使用双引号引起来
System.out.println(num);
System.out.println(num2);
System.out.println(gender);
System.out.println(str);
}
}
5.3变量的分类
- 基本数据类型变量
- 引用数据类型变量
- 按被声明的位置划分:
- 局部变量:方法或语句块内部定义的变量
- 成员变量:方法外部、类的内部定义的变量
6.运算符
6.1 算数运算符
++:
如果是变量前缀:先对此变量加1,再执行其他的操作。
如果是变量后缀:先执行当前语句的其他操作,再对此变量加1
-- :
如果是变量前缀:先对此变量减1,再执行其他的操作。
如果是变量后缀:先执行当前语句的其他操作,再对此变量减1
6.2 赋值运算符
赋值运算符作用是将一个值赋给一个变量,运算顺序从右到左
6.3 关系运算符
关系运算符作用是比较两边的操作数,结果总是boolean型的。
6.4 逻辑运算符
逻辑运算符用于对boolean
型结果的表达式进行运算,运算结果总是boolean型,后面结合条件结构讲解。
6.5 字符串连接运算符
String s="He" + "llo";//结果"Hello"
//"+"除了可用于字符串相连接,也能将字符串与其它的数据类型相连成一个新的字符串。
//如:
String s="x" + 123;// 结果"x123"
6.5 位运算符
位运算符对两个操作数中的每一个二进制位都进行运算
位运算符功能:
- 按位取反 ~
- 按位与 &
- 按位或 |
- 按位异或 ^
移位运算符
左移:"a<<b; "将二进制形式的a逐位左移b位,最低位空出的b位补0
带符号右移:"a>>b; "将二进制形式的a逐位右移b位,最高位空出的b位补原来的符号位
无符号右移:"a>>>b;"将二进制形式的a逐位右移b位,最高位空出的b位补0
a % b = a & (b-1),b是2的n次幂
7.程序流程控制
7.1 顺序结构
顺序结构是一种基本的控制结构,它按照语句出现的顺序执行操作。
7.2 分支结构
分支结构又被称为选择结构,根据条件成立与否来执行操作
7.2.1 if语句
/*语法:
if(条件)
{ 条件成立执行的内容 }
*/
7.2.2 if - else 选择结构
/*语法:
if(条件)
{
条件成立执行的内容
}else{
条件不成立执行的内容
}
*/
7.2.3 switch选择结构
替换等值判定的多重选择结构
switch(表达式){
case 值1://相当于if(){}
//满足表达式的结果==值1的时候执行的代码
break;//退出当前的switch选择结构
case 值2://相当于else if(){}
//满足表达式的结果==值2的时候执行的代码
break;
......
case 值n:
//满足表达式的结果==值n的时候执行的代码
break;
default://相当于else
//以上的匹配都失败的时候执行的内容
break;
}
/*
表达式的返回值必须是下述几种类型之一:int, byte, char, short,String;
case子句中的取值必须是常量,且所有case子句中的取值应是不同的;
default子句是可选的;
break语句用来在执行完一个case分支后使程序跳出switch语句块;如果case后面没有写break则直
接往下面执行!
Case后面的执行体可写{ }也可以不写{ }
*/
8.字符串比较相等
- ==比较内存地址
- equlas比较内容
String str1="abc";
String str2=input.next();
System.out.println(str1==str2);
System.out.println(str1.equlas(str2));
9.循环结构
1.while循环
while(循环条件){
//循环操作
}
while循环的特征:
1.先判断后执行
2.如果初始条件不满足,循环一次都不执行
2.do - while 循环
do{
//循环操作
}while(循环条件);
//PS:特别注意最后的分号不能丢掉
powershell do - while 特征:
- 先执行后判断
- .如果初始条件不满足,循环至少执行一次
3. for 循环
for(表达式1:定义循环变量;表达式2:循环条件;表达式3:循环变量的变化){
//循环操作
}
1、先执行表达式1:定义循环变量,该表达式只执行一次
2、判断循环条件是否成立:
3、循环条件成立:执行循环操作, 不成立:退出循环
4、执行完循环操作,执行表达式3–循环变量的变化
5、重复2-4步骤
for 的特征:
先判断后执行
4. 循环中的关键字
4.1 break
作用:退出循环
4.2 continue
作用:结束当次循环,进入下一次循环
4.3三种循环对比
10. 数组
1.一维数组
数组(Array),是多个相同类型数据按一定顺序排列的集合,并使用一个名字命名,并通过编号的
方式对这些数据进行统一管理。
变量:在内存开辟一块空间。
数组:也是变量,在内存中开辟一串连续的空间。数组长度一旦确定,就不能修改
1.1基本用法
- 格式
- 格式 1. 数据类型[] 数组名,属于只创建了数组引用名, 并未在内存创建数组空间
- 格式 2. 数据类型[] 数组名称 = new 数据类型[数组长度];
- 格式 3. 数据类型[] 数组名称 = {数组内容 1,数组内容 2,数组内容 3…数组内容 n};
- 格式 4. 数据类型[] 数组名称 = new 数据类型[]{内容 1,内容 2,内容 3…内容 n}
代码案例:
public static void main(String[] args) {
// 1、声明数组
//1.1 先声明,后开辟空间:数据类型[] 数组名称;
int[] arr1;//声明
arr1=new int[4];//开辟空间:数组名称=new 数据类型[数组长度]; 数组的长度一旦
确定,不可以改变
//1.2 声明数组的同时开辟空间
double[] arr2=new double[5];
System.out.println(arr1);
System.out.println(arr2);
//1.3 声明数组
String[] arr3;//只是声明,没有在内存中开辟数组空间
//System.out.println(arr3);
//1.4 声明数组的同时赋值 数据类型[] 数组名称=new 数据类型[]{数组值1,数组值2,
数组值3,数组值4......数组值n};
//此时数组的长度由赋值的个数决定
int[] arr4=new int[]{1,2,3,4,5};
System.out.println(arr4);
//1.5 声明数组的同时赋值 数据类型[] 数组名称={数组值1,数组值2,数组值3,数组值
4......数组值n};
//此时数组的长度由赋值的个数决定
double[] arr5= {1.5,3.6,5.8,4.0};
}
1.2内存结构
从图中知道:
arr2数组第一次指向 0x32ab这个地址
但是经过 arr2 =new String [5] 这个命令之后 就会重新进行指定指向0x78cd
那么原来的0x34ab这个地址呢就会不用,到最后被gc回收。
下图是JMM
也就是jvm的内存结构
但是图中的常量池这个位置在jdk 1.7中是这个位置且容量是固定的
但在jdk 1.8中 这个常量池是在 堆中且不需要限制容量
需要注意的是,在 JDK 1.8 中,元空间的大小默认是不受限制的,它会根据应用程序的需要动态调整。当然,也可以通过设置 JVM 参数来限制元空间的大小,以避免过度使用本地内存。
在 JDK 1.7 中,常量池位于永久代(Permanent Generation),也称为方法区(Method Area)。永久代是JVM 使用的一块特殊内存区域,用于存储类的元数据信息、常量池、静态变量等。但是在 JDK 1.7 中,常量池的容量是固定的,并且能容纳的常量数目较少。
而在 JDK 1.8 中,永久代被移除,取而代之的是元空间(Metaspace)。元空间不再位于虚拟机内存中,而是使用本地内存。因此,JDK1.8 中的常量池不再存放在永久代中,而是存放在堆中的一个叫做运行时常量池的数据结构中。
2.二维数组
对于二维数组的理解,我们可以看成是一维数组array1又作为另一个一维数组array2的元素而存在。其实,从数组底层的运行机制来看,其实没有多维数组
语法格式:
格式1:
int[][] arr = new int[3][2] 1
- 定义了名称为arr的二维数组
- 二维数组中有3个一维数组
- 每一个一维数组中有2个元素
- 一维数组的名称分别为arr[0], arr[1], arr[2]
- 给第一个一维数组1脚标位赋值为78写法是: arr[0][1] = 78
格式2:
int[][] arr = new int[3][];
- 二维数组中有3个一维数组。
- 每个一维数组都是默认初始化值null (注意:区别于格式1)
- 可以对这个三个一维数组分别进行初始化 arr[0] = new int[3]; arr[1] = new int[1];
arr[2] = new int[2];
注: int[][] arr = new int[][3] 非法
格式3:
int[][] arr = new int[][]{{3,8,2},{2,7},{9,0,1,6}};
- 定义一个名称为arr的二维数组,二维数组中有三个一维数组
- 每一个一维数组中具体元素也都已初始化
- 第一个一维数组 arr[0] = {3,8,2};
- 第二个一维数组 arr[1] = {2,7};
- 第三个一维数组 arr[2] = {9,0,1,6};
- 第三个一维数组的长度表示方式:arr[2].length;
3.二维数组的内存结构:
4. 数组中常见算法
1. 二分查找
数组的复制、反转、查找(线性查找、二分法查找)
public static void main(String[]args){
String[]arr=new String[]{"JJ","DD","MM","BB","GG","AA"};
//数组的复制(区别于数组变量的赋值:arr1 = arr)
String[]arr1=new String[arr.length];
for(int i=0;i<arr1.length;i++){arr1[i]=arr[i];
}
//数组的反转
// for(int i = 0,j = arr.length - 1;i < j;i++,j--){
// String temp = arr[i];
// arr[i] = arr[j];
// arr[j] = temp;
// }
//遍历
for(int i=0;i<arr.length;i++){
System.out.print(arr[i]+"\t");
}
System.out.println();
//查找(或搜索)
//线性查找:
String dest="BB";
dest="CC";
boolean isFlag=true;
for(int i=0;i<arr.length;i++){
if(dest.equals(arr[i])){
System.out.println("找到了指定的元素,位置为:"+i);
isFlag=false;
break;
}
}
if(isFlag){
System.out.println("很遗憾,没有找到的啦!");
}
//二分法查找:(熟悉)
//前提:所要查找的数组必须有序。
int[]arr2=new int[]{-98,-34,2,34,54,66,79,105,210,333};
int dest1=-34;
dest1=35;
int head=0;//初始的首索引
int end=arr2.length-1;//初始的末索引
boolean isFlag1=true;
while(head<=end){
int middle=(head+end)/2;
if(dest1==arr2[middle]){
System.out.println("找到了指定的元素,位置为:"+middle);
isFlag1=false;
break;
}else if(arr2[middle]>dest1){
end=middle-1;
}else{//arr2[middle] < dest1
head = middle + 1;
}
}
if(isFlag1){
System.out.println("很遗憾,没有找到的啦!");
}
}
2.排序算法
衡量排序算法的优劣:
- 时间复杂度:分析关键字的比较次数和记录的移动次数
- 空间复杂度:分析排序算法中需要多少辅助内存
- 稳定性:若两个记录A和B的关键字值相等,但排序后A、B的先后次序保持不变,则称这种排序算
法是稳定的
3.冒泡排序
public static void main(String[]args){
int[]arr=new int[]{43,32,76,-98,0,64,33,-21,32,99};
//冒泡排序
for(int i=0;i<arr.length-1;i++){
for(int j=0;j<arr.length-1-i;j++){
if(arr[j]>arr[j+1]){
int temp=arr[j];
arr[j]=arr[j+1];
arr[j+1]=temp;
}
}
}
for(int i=0;i<arr.length;i++){
System.out.print(arr[i]+"\t");
}
}
这里的 arr.length - i 是为了把已经排好的数据减去,避免重复排序,节省时间。
Arrys工具类
1.判断数组是否相等
boolean equals(int[] a,int[] b):判断两个数组是否相等
int[] arr1 = new int[]{1,2,3,4};
int[] arr2 = new int[]{1,2,3,4};
boolean isEquals = Arrays.equals(arr1, arr2);
System.out.println(isEquals);
2.输出数组信息
String toString(int[] a):输出数组信息
System.out.println(Arrays.toString(arr1));
3.将指定值填充到数组之中
void fill(int[] a,int val):将指定值填充到数组之中
Arrays.fill(arr1,10);
System.out.println(Arrays.toString(arr1));
4.对数组进行排序
void sort(int[] a):对数组进行排序
Arrays.sort(arr2);
System.out.println(Arrays.toString(arr2));
5.查找指定值
int binarySearch(int[] a,int key)
int[] arr3 = new int[]{-98,-34,2,34,54,66,79,105,210,333};
int index = Arrays.binarySearch(arr3, 210);
if(index >= 0){
System.out.println(index);
}else{
System.out.println("未找到");
}
}
方法的存储详解
以下面一段代码来看看方法中的变量是如何存储的
public static void f1() {
int i = 1;
i = 11;
int[] arr = new int[4];
arr[1] = 2;
f2();
}
public static void f2() {
int i = 2;
char a = 'a';
}
public static void main(String[] args) {
int i = 3;
f1();
char a = 'b';
}
可以看到不管是main方法还是f1()方法还是f2()方法都是在栈中存储的。
方法中的局部变量存储在java虚拟机的栈内存中。
栈(Stack)是一种线性数据结构,用于存储方法的局部变量、方法参数、临时数据等。每个线程在执行方法时都会创建自己的栈帧(Stack Frame),栈帧包含了方法的参数和局部变量的值。当方法被调用时,会在栈上为该方法创建一个新的栈帧,在方法执行完毕后,栈帧被销毁。
还可以看到在栈中arr只是有一个指向堆中数据的指针。
String 字符串的一些误区
System.out.println(Objects.toString(str1))
打印的结果不是对象的地址,而是对象的内容。
这是因为Objects.toString() 方法会检查传入的对象是否为 null,如果为null,则返回字符串 “null”;否则,返回对象的 toString() 方法的结果。
str1 为一个 String 类型的对象。String 类已经重写了 toString() 方法,使其返回字符串对象本身的内容而不是地址。因此,Objects.toString(str1) 实际上等同于直接打印 str1 对象。
>>>与>>
对于正数来说,>> 和 >>> 的效果相同;而对于负数来说,>> 右移会保留符号位,而 >>> 右移会将符号位也当作普通位进行处理,
在计算机中,正数和负数都是以二进制的补码形式存在