JAVA简介
引用数据类型
字符串-String
字符串类型String是我们开发中最常使用的引用类型,我们用双引号引起来的内容就是字符串
字符串的拼接
- Java的编译器对字符串进行特殊处理,可以使用+连接任意字符串和其他数据类型,简化字符串的处理
- Java的编译器对字符串进行特殊处理,可以使用+连接任意字符串和其他数据类型,简化字符串的处理
eg:int age=25;String s = "age is " + age;
常用字符串方法
-
获取字符串长度:public int length()
-
查找指定下标的字符:public char charAt (int index)
-
字符串的比较方法
a.equals(b)方法比较public boolean equals(Object c)是否完全相同
忽略大小写比较用publicboolean equalslgonreCase()
String s4 = "abcd"; String s5 = "abcD"; System.out.println("s4和s5是否相等:" + s4.equals(s5)); System.out.println("s4和s5忽略大小是否相等:" + s4.equalsIgnoreCase(s5));
-
大小写转换
转化大写:Stringupper=str.toUpperCase();
转化小写:Stringlower=str.toLowerCase();
String s5 = "abcD"; System.out.println("将s5都转为小写" + s5.toLowerCase()); System.out.println("将s5都转为小写" + s5.toUpperCase());
字符串的截取方法
publicStringsubstring(intindex):截取从参数位置一直到字符串末尾,返回新字符串。[begin,end]
publicStringsubstringintbegin,intend):截取从begin开始,一直到end结束,中间的字符串[begin,end)
String s6 = "210623200102030405";
String s7 = s6.substring(14);
System.out.println(s7);
//输出0405
String s8 = s6.substring(6,14);
System.out.println(s8);
//输出20010203
字符串的特点
字符串的内容永远不变
String s="hello";
System.out.println(s);
//显示hello
s="world";
System.out.println(s);
//显示world
//变的不是字符串,而是变量指向,s指向了world
原来的字符串"hello"还在,只是我们无法通过变量s访问它而已。因此,字符串的不可变是指字符串内容不可变。
空值null
-
引用类型的变量可以指向一个空值null,它表示
不存在,尚未分配内存空间,即该变量不指向任
何对象
-
String s1 = “”; // 空字符串,已经分配内存空间
System.out.println(s1); // 空
System.out.println(s1.length()); // 0
-
String s2 = null; // 空值,尚未分配内存空间
System.out.println(s2); // null
System.out.println(s2.length()); // 空指针异常
-
空值null和空字符串的区别
- 空间:null未分配空间,空字符串已经分配内存空间
- 长度:s2.length()空指针异常,s1.length()长度为0
数组
为什么用数组
如果说程序中,需要存储大量的相同类型的一组数据,如果直接使用变量来进行存储,每个变量只能存储一个值,就需要大量的变量。
- 代码过于臃肿
- 程序的可读性差
- 数据维护较差
- 变量的容器小,数组的容器大
数组的定义
- 一组相同数据类型的数据,在内存中开辟连续的内存空间,按照一定的先 后次序排列组合而成。
- 其中 ,每一个数据称作一个元素 ,每个元素可以通过一个索引(下标)来访问它们
数组声明
- 数据类型[] 数组名
- 数据类型 数组名[]
注意:
- 声明的时候并没有实例化任何对象,只有在实例化数组对象时,JVM才分配空间,这时才与长度有关
- 声明一个数组的时候并没有数组 真正被创建
- 构造一个数组,必须指定长度
数组的初始化
初始化
所谓初始化就是给数组中的数组元素分配内存空间,并为每个数组元素赋初始值
-
默认初始值:
int:0 float:0.0 boolean:false String:null char:不可见的字符
**注意:**java中的数组必须先初始化然后才可以使用(只声明不能使用)
初始化方式
动态初始化
-
类型[] 数组名=new 类型[长度] 声明并创建数组
-
先声明,再创建(分配内存空间)
数据类型[] 数组名;
数组名 = new 数组类型[长度];
//1
String[] arr8=new String[3];
//2
String[] a;
a = new String[5];
静态初始化
类型[] 数组名 = new 类型[]{元素0,元素1,…}
int[] a = new int[]{1,2,3,4,5,6,7}
//括号中不写长度
直接指定初始化元素,这样就不必写出数组大小,而是由编译器自动推算数组大小
静态省略初始化
数组类型[] 数组名 = {元素0,元素1,…}
int[] a ={1,2,3,4,5,6,7}
省略静态初始化不能拆分为两个步骤
访问数组元素
- 取值
取值格式:数据类型 变量名 = 数组名[索引值];
索引值的取值范围:0到数组长度-1
- 赋值
赋值格式:数组名[索引值] = 对应类型的值;
- 可能出现的问题
数组索引值越界异常:ArrayIndexOutOfBoundsException;
引用数据类型的值为null,继续对变量(对象)进行操作,这时候就会报错,空指针异常:NullPointerException;
数组的内存分析
数组不可变性,同理String类型字符串,改变的是指向
-
栈
存放基本数据类型的变量,以及引用变量的引用
特点:函数中的变量所占用的空间,执行后就会销毁
-
堆
new出来的东西,执行后堆内存的内容(对象)会被标记垃圾,等带垃圾回收机制来回收
数组中的特殊值0和null
-
针对数组本身
-
int[] a = new int [0];
-
int[] b = new int[]{};
-
数组为null,系统没有为其分配内存空间(没有地方只能流浪)int[] c = null;
-
-
数组元素内容
数组元素的值为0或者为null(即默认初始化时侯为空,或者里面素服赋值为n)
Arrays工具类
-
将数组转化为字符串
static String toString(int[] a);
Arrays.toString()方法返回指定数组内容的字符串表示形式
-
填充
-
排序
-
查找
-
拷贝
-
比较长短
数组的特点:
- 定长。容量固定。数组一旦创建后,那么长度不能更改。(容量,长度,都是指存储的数量)
- 存储的数据类型 (可以是任意数据类型)必须都一致。不允许出现混合类型
- 在内存中空间连续
- 数组是引用类型的数据,存在栈和堆的地址引用关系。数组变量属于引用类型 ,数组也是对象
输入和输出语句
输出
print()、println() 和printf()区别
- print():不换行,里面需要写内容
- printlf():自动换行,可以不写内容
- pritnf():不换行,格式化输出,占位
格式化符号
- %d:整数类型占位
- %f:浮点类型占位
- %s:各种类型占位
- %e:浮点类型科学计数法占位
- %x:整数类型十六进制占位
输入
Scanner输入导入import java.util.Scanner;
Scanner sc = new Scanner(System.in);
//数据类型 变量名 = sc.next*();
*可选:
int:.nextInt();
double:.nextdouble();
String:.nextLine();
流程控制
流程控制:用来控制程序中的各语句执行程序的语句,可以把语句组合成能完成一定功能的小逻辑模块
顺序
顺序结构代表先执行a,再执行b的逻辑,按照顺序执行
System.out.println("111");
System.out.println("222");
//运行结果
//111
//222
选择(分支)
if语句
区间判断
单分支
语法格式if…
if(布尔表达式1){
表达式1成立时,执行代码块
}
注意事项:
- 代码块中可以包含多条语句
- if语句块只有一行语句时,可以省略花括号
双分支
语法格式if…else…
if(布尔表达式1){
条件1成立时,需要做的事情
}else{
条件1成立时,需要做的事情
}
注意事项:
- 在语法结构中,else并不是必须的
- else语句块只有一行语句时,可以省略号{}
多分支
语法格式if…else if …else
if(布尔表达式1){
表达式1成立时,需要做的事情
}else if(布尔表达式2){
表达式2成立时,需要做的事情
}else{
不满足上述所有条件做的事情
}
注意事项:
-
使用多个if判断时,需要注意判断顺序(if语句执行顺序是从上到下执行
正确的方式是按照判断范围从大到小依次判断或者改写成从小到大依次判断
-
注意边界条件
-
浮点数判断:由于浮点数在计算机中常常无法精确表示,并且计算可能出现误差,因此,判断浮点数相等用==判断可能存在不准确的情况。
-
引用数据库类型使用equals判断相等
注意事项
- 前面条件为true执行完前面的代码块则代码就结束了。取消 && 条件
- 语句交度else可不写
- 从语法角度,if后面的大括号可以省略情况(if内语句块只有一条语句)同理else也相同
switch语句
等值判断,多分支选择语句
语法格式
switch(表达式){
case 常量表达式1:语句1;break;
case 常量表达式2:语句2;break;
case 常量表达式3:语句3;break;
default:
语句;
}
-
switch:判别表达式取值类型
- 基本数据类型:byte,short,int,char
- 引用数据类型:String(jdk7后添加),枚举
-
csae后面常量不可重复
-
default默认执行
-
语法具有穿透性(向下穿透性时不进行比较)
-
break作用:跳出switch结构(可阻断穿透)
if与switch区别
- 判断条件:
- if:区间判断或等值判断
- switch:等值判断
- 等值判断效率:switch效率更高,switch直接取值跳到对应case,不需要层层判断
循环
“循环结构”代表“如果… ,则重复执行…”的逻辑。就是让计算机根据条件做循环计算,在条件满足时继续循环,条件不满足时退出循环。
while循环
语法:
while(条件判断){
//循环体,条件为为真执行
//自加语句!!!eg:i++;
}
注意:
- 先判断再执行
- 自加语句一定得有,用于循环结束条件,避免产生死循环
do……while循环
语法:
do{
//循环体
//自加语句
}while(条件判断)
注意:
- 先执行一次再判断是否执行后续循环,条件拍段为真执行,为假则结束
- 自加语句
for循环
语法:
for(初始化变量;条件判断为true;步进语句){
//循环体
}
//省略条件判断语句:死循环
//省略条件判断语句和步进判断语句:死循环
//省略初始化变量、条件判断、步进语句:死循环
foreach方法语法:
for(数据类型 元素名:变量名){
//循环体
}
//eg:foreach方法遍历数组
String[] array = {"Java","C++","vscode","web"};
for(String s:array) {
System.out.println(s);
}
注意:
- 执行顺序:初始化变量—>判断条件—>循环体—>步进语句
- 初始化变量只执行一次,后面每次循环不进行初始化
- 如果条件判断取消了默认为true
- 循环体只有一条语句可以省略花括号
- 初始化变量、条件判断、步进语句都可省略,分号不能省略
循环使用场景:
- for循环一般执行循环次数固定且明确的
- while循环与do…while循环更适合不固定次数的循环,因为它们的语法结构更容易表达一个boolean类型的条件表达式
循环控制语句:
-
break:break(在循环和switch中都是结束循环或者分支语句的意思)
起别名braek:
f1:for( ; ;) //f1起别名 { break f1; //停止f1对应的此层循环 }
-
countinue:countinue 结束本次循环(不执行循环体内continue以后的代码,for,去执行步进语句while或者do while去执行条件判断)
数组操作
数组遍历
所谓遍历,是指沿着某条搜索路线,依次对树(或图)中每个节点均做一次访问
两种方法
-
for循环
实现案例:
public class main{ public static void main(String[] args){ int[] ns = { 1, 4, 9, 16, 25 }; for(int i = 0; i < ns.length;i++) //i<ns.length或i<=ns.length-1 { int n = ns[i]; System.out.println(n); } } }
for循环通过索引来访问,即数组的下标
- 索引值从0开始
- 索引值不能超出范围
-
foreach循环
实现案例:
public class Main{ public static void main(String[] args){ int[] ns = { 1, 4, 9, 16, 25}; for(int n : ns ){ System.out.println(n); } } }
foreach循环直接迭代数组的每个元素,而不是索引值
- foreach更加简洁
- foreach无法拿到数组的索引值
冒泡排序
- 冒泡排序步骤
- 比较相邻的元素。如果第一个比第二个大,就交换位置。
- 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。这步做完后,最后的元素会是最大的数。
- 针对所有的元素重复以上的步骤,除了最后一个元素。
- 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。
- 冒泡排序特点
每一轮循环后,最大的一个数被交换到末尾,因此,下一轮循环就可以“排除”最后的数(不参与),每一轮循环都比上一轮循环的结束位置靠前一位。
-
记忆
N个数字排序 俩俩比较小考前 总共比较N-1轮(外部循环) 每轮比较N-1-i(内部循环)
-
冒泡排序案例
public static void main(String[] args) { int[] ns = {3,2,4,5,10,11,128,7,8,6}; int length = ns.length; for(int i = 0;i<length-1;i++) { for(int j = 0;j<length-1-i; j++) { if(ns[j]>ns[j+1]) { int temp = ns[j]; ns[j] = ns[j+1]; ns[j+1] = temp; } } } System.out.println("冒泡排序后"); System.out.println(Arrays.toString(ns)); }
-
Arrays工具类排序
Arrays.sort()方法;
import java.util.Arrays; //导入Arrays工具类 public class Test05 { public static void main(String[] args) { int[] ns = {3,2,4,5,10,11,128,7,8,6}; System.out.println("排序前"+Arrays.toString(ns)); Arrays.sort(ns); //默认升序 System.out.println("排序后"+Arrays.toString(ns); } }
折半查找/二分查找
用于查找元素
-
可以通过循环遍历或者Arrays工具类两种方式进行查找
-
Arrays.binarySearch(a, key)方法
import java.util.Arrays; //导入Arrays工具类 public class Test01 { public static void main(String[] args) { int[] a = {1,2,3,4,5,6,7}; int i = Arrays.binarySearch(a, 5); //(Object[],Object) //第一个参数写数组名,第二个参数写目标元素 System.out.println(i); //输出元素5的下标 } } //若找不到则i输出为负数
-
双指针遍历查找:通过两个下标分别从数组得头部和尾部,同时 对该数组进行遍历,将数组中的每个元素与指定元素进行比较
-
折半查找:前提数组有序,如果需要进行指定元素的查找,可以用折半查找算法,提高查找元素的效率
实现案例:
// 折半查找(二分查找)查找数组元素11 public static void main(String[] args) { int[] a = {2,3,4,5,6,7,8,9,11,12,16,19}; int target = 11; int index = -1; int low = 0; int height = a.length; while(low <=height) { int mid = (low+height)/2; if(target == a[mid]) { index = mid; break; } if(target> a[mid]) { low +=1; } if(target < a[mid]) { height = height-1; } } System.out.println("下标是"+index); }
-
总结:
- 查找指定元素分为两种情况:无序数组查找和有序数组查找
- 无序数组可以通过遍历数组或Arrays工具类两种方式查找指定元素
- 有序数组可以通过折半查找查找指定元素
洗牌算法
用于数组乱序
实现案例:
public static void main(String[] args) {
int[] a = {0,1,2,3,4,5,6,7,8,9,10};
for (int i = 0; i < a.length-1; i++) {
int index = (int)(Math.random()*i);
int temp = a[i];
a[i] = a[index];
a[index] = temp;
}
System.out.println(Arrays.toString(a));
}
//实现步骤:
//有数组a等待乱序
//从a中随机抽取一个为乱序的元素
//将该元素与数组a中最后一个为乱序的元素交换
//重复上两步
数组合并
用于数组合并
-
无序数组:复制元素方式
-
循环方法
-
System.arraycopy()方法
import java.util.Arrays; public class Test09 { public static void main(String[] args) { int[] ns = {1,2,3,4,5}; int[] newns = new int[10]; System.arraycopy(ns, 2, newns, 4, 3); System.out.println(Arrays.toString(newns)); //参数一:被复制数组名 //参数二:被复制的起始元素下标 //参数三:复制后的新数组 //参数四:新数组存放复制元素的起始 //参数五:复制的长度 } } //输出结果:[0, 0, 0, 0, 3, 4, 5, 0, 0, 0]
-
-
有序数组:
-
循环方法: 实现案例
public static void main(String[] args) { int[] a1 = {1,3,5,7,9}; int[] a2 = {2,4,6,8}; int[] a3 = new int[a1.length+a2.length]; for(int i=0;i<a1.length;i++) { a3[i]=a1[i]; } for(int j =a1.length,i=0 ;j<a1.length+a2.length; j++,i++) { a3[j] = a2[i]; } System.out.println(Arrays.toString(a3)); }
-
双指针: 实现案例
public static void main(String[] args) { int[] a1 = {1,3,5,7,9}; int[] a2 = {2,4,6,8}; int L = a1.length + a2.length; int[] a3 = new int[L]; for (int i = 0,j =a1.length; i<a1.length||j<L ; i++,j++) { if(i<a1.length) { a3[i] = a1[i]; } if(j<L) { a3[j] = a2[j-a1.length]; } } System.out.println(Arrays.toString(a3)); }
-
-
有序数组合并且合并后数组有序: 前提合并的两个数组有序
实现案例
//合并数组且合成后的数组有序 int[] a1 = {2,4,6,8,10}; int[] a2 = {1,3,5,7,9}; int[] a3 = new int[a1.length+a2.length]; int i=0,j=0,k=0; while (i<a1.length&&j<a2.length) { if(a1[i]<a2[j]) { a3[k] = a1[i]; i++; k++; }else if(a1[j]>a2[i]) { a3[k] = a2[j]; j++; k++; } } //存放剩余的 if(i<a1.length) { for(int m=i; m<a1.length ; m++) { a3[k] = a1[m]; k++; } } if(j<a2.length) { for(int n=j; n<a2.length ;n++) { a3[k] = a2[j]; k++; } } System.out.println(Arrays.toString(a3));
数组旋转
-
循环
public class Test01 { public static void main(String[] args) { int[] ns = {1,3,4,5,6,7,8}; int[] newns = new int[7]; int temp=4; for(int i = 0;i<ns.length;i++) { newns[(i+temp)%ns.length] = ns[i]; //核心思想 } System.out.println(Arrays.toString(newns)); } } //输出结果:{6,7,8,1,3,4,5}
-
双指针循环(冒泡排序思想)
//右旋转 public class Test02 { public static void main(String[] args) { int[] ns = {1,3,5,7,9}; int count = 3; //右旋转位数 for(int i = 0;i<count;i++) { for(int j=ns.length-1 ; j>0 ;j--) { int temp = ns[j]; ns[j] = ns[j-1]; ns[j-1] = temp; } } System.out.println(Arrays.toString(ns)); } } //输出结果:{7,9,1,3,5}
二维数组操作
public class Test04 {
public static void main(String[] args) {
int[][] a = {
{1,2,3,12,34,9},
{2,3,4,6,7,8,},
{3,4,5}
};
//二维数组赋值,可不写元素,但是逗号“,”不能省略
System.out.println(Arrays.deepToString(a));
//Arrays方法输出二维数组
System.out.println("--------------------");
for(int i=0;i<a.length;i++) {
for(int j=0;j<a[i].length;j++) {
System.out.printf(" %d " ,a[i][j]);
}
System.out.println();
}
//for循环输出二维数组
System.out.println("-------------------");
for (int[] is : a) {
for (int i : is) {
System.out.printf(" %s ",i);
}
System.out.println();
}
//foreach循环输出二维数组
}
}
public class Test01 {
public static void main(String[] args) {
int[] ns = {1,3,4,5,6,7,8};
int[] newns = new int[7];
int temp=4;
for(int i = 0;i<ns.length;i++) {
newns[(i+temp)%ns.length] = ns[i];
//核心思想
}
System.out.println(Arrays.toString(newns));
}
}
//输出结果:{6,7,8,1,3,4,5}
-
双指针循环(冒泡排序思想)
//右旋转 public class Test02 { public static void main(String[] args) { int[] ns = {1,3,5,7,9}; int count = 3; //右旋转位数 for(int i = 0;i<count;i++) { for(int j=ns.length-1 ; j>0 ;j--) { int temp = ns[j]; ns[j] = ns[j-1]; ns[j-1] = temp; } } System.out.println(Arrays.toString(ns)); } } //输出结果:{7,9,1,3,5}
二维数组操作
public class Test04 {
public static void main(String[] args) {
int[][] a = {
{1,2,3,12,34,9},
{2,3,4,6,7,8,},
{3,4,5}
};
//二维数组赋值,可不写元素,但是逗号“,”不能省略
System.out.println(Arrays.deepToString(a));
//Arrays方法输出二维数组
System.out.println("--------------------");
for(int i=0;i<a.length;i++) {
for(int j=0;j<a[i].length;j++) {
System.out.printf(" %d " ,a[i][j]);
}
System.out.println();
}
//for循环输出二维数组
System.out.println("-------------------");
for (int[] is : a) {
for (int i : is) {
System.out.printf(" %s ",i);
}
System.out.println();
}
//foreach循环输出二维数组
}
}