JAVA基础
- 1、进制与转换
- 2、基本数据类型
- 3、运算符
- 4、流程控制
- 5、一维数组
- 6、二维数组
- 7、方法(函数)
- 8、面向对象
- 9、API
- 10、正则表达式
- 11、Math类
- 12、包装类
- 13、日期类
- 14、异常
- 15、集合
- 16、泛型
- 17、Stream
- 18、Map
- 19、File
- 20、IO
-
- 文本文件/非文本文件
- 字符流:FileReader
- 字符流:FileWriter
- 字符流:FileReader/FileWriter的文件复制
- 字节流:FileInputStream
- 字节流:FileInputStream/FileOuputStream文件复制
- 处理流:BufferedInputStream/BufferedOutputStream文件复制
- 处理流:BufferedReader/BufferedWriter文件复制
- 20.1 流的异常处理
- 20.2 转换流
- 20.3 合并流
- 20.4 System类对IO流的支持
- 20.5 数据流
- 20.6 对象流:序列化/反序列化流
- 20.7 Properties
- 21、线程
- 22、网络编程
- 23、枚举
- 24、Junit单元测试
- 25、反射
- 26、JVM
1、进制与转换
-
二进制向十进制转化:从低位次(数字最右边是低位次)开始,按位次乘以2的位次次幂,然后求和。其他进制向十进制转化与二进制类似。
-
二进制转8进制:从低位次开始,每三位进制划分一组,产生8进制的数,然后连起来,就得到一个八进制的数
-
二进制转16进制:从低位次开始,每四位进制划分一组,产生16进制的数,然后连起来,就得到一个16进制的数
15*4 = 120 几进制?
注意:
绝大部分小数转化为二进制是表示不精确的,导致计算机在存储小数的时候是不精确的。
数据的原、反、补码过程
- 任何数据在计算机中存储的是补码;
- 规定最高位是一个符号位,0表示正数,1表示负数;
- 直接算出来的二进制数字称为原码;
- 规定正数的原码、反码、补码是一致的;
- 负数的反码是在原码的基础上,最高位不变,其余的1变0、0变1;
- 负数的补码是在反码的基础上+1;
- 规定用-0表示当前类型的最小值
2、基本数据类型
2.1 变量
变量:由变量名、数据类型、值三部分组成。它是记录数据存储的容器
2.2 基本类型
在计算机中,是以二进制形式来存储数据,每一位二进制在内存中称之为是一“位”(bit,简写b),8位 = 1字节(byte,简写B);1024B=1KB、MB、GB。。。。
2.2.1 基本数据类型
- 数值型
-
- 整数型
-
-
- byte(字节型) ,1个字节,取值范围-128~127;
-
-
-
- short(短整型),2个字节,取值范围-32768~32767;
-
-
-
- int(整型),4个字节,取值范围-20亿~20亿,在java中整数默认为
int
类型;
- int(整型),4个字节,取值范围-20亿~20亿,在java中整数默认为
-
-
-
- long(长整型), 8个字节,需要在结尾添加l/L作为标识;
-
-
- 浮点型
-
-
- float(单精度),4个字节,虽只有四个字节,但取值范围要比long还要大,需在结尾添加f/F标识
-
-
-
- double(双精度),8个字节,在java中小数默认为
double
类型
- double(双精度),8个字节,在java中小数默认为
-
- 字符型
-
- char(字符型),2个字节,取值范围0~65535,字符在存储过程中需要按照某种规则转化为数字,这种转化规则称之为编码。ASCI表取值范围0~127
-
-
- ISO-8859-1(西欧码表),1个字节 表示1个字符
-
-
-
- 所有的码表必须兼容西欧码表,前256个字符是一样的,而且前256个字符永远占用1个字节。
-
-
-
- gb2312(国标码),gbk兼容gb2312,2个字节 表示1个字符,收录了绝大部分常见的简体汉字和一部分的繁体字。
-
-
-
Unicode
编码体系(万国码):UTF-8,3个字节表示1个字符;utf-16,2个字节表示1个字符
-
- 布尔型:boolean,4个字节
数据类型转换
隐式类型转换
-
小的类型可以自动转化为大的类型,自动类型转换, 也叫隐式类型转换
-
整数可以自动转化为小数,但是可能产生精度损失
-
字符可以自动转化为整数
-
- 如short = ‘a’;char c = 97; 直接赋值不会报错
-
- 如char c = ‘a’;short s = c; 不可以这样赋值。原因是short类型和char类型范围没有完全重合,不能完全包含。
- 如char c = ‘a’;short s = c; 不可以这样赋值。原因是short类型和char类型范围没有完全重合,不能完全包含。
显式类型转换
- 大的类型给小的类型转换,也叫强制类型转换
注意:
大类型可以强制转为小类型,但是在转化的时候因为字节的损失所以可能导致数据
不准确。
2.2.2 引用数据类型
数组:[]、字符串:String、类:class、接口:interface
注意
:字符串
+ 任何数据 = 字符串
;
3、运算符
3.1 算术运算符
- byte/short/char在运算的时候会自动提升为int
- int 在计算完成之后结果一定是 int
- 小类型和大类型运算的时候结果一定是大类型
- 整数/0 ,会报算术异常
- 非零小数/0、非零数字/0.0,得到的结果为Infinity
- 0/0.0、0.0/0、0.0/0.0,得到的结果为NaN
- %的结果的符号看的是%左边数字的符号
- ++/–自增/自减,在变量之前:
先自增再赋值
/先自减再赋值
,在变量之后:先赋值再自增
/先赋值再自减
- byte/short/char可以参与自增/自减运算,运算之后结果类型不变
3.2 赋值运算符
- 除了
=
之外,其他的符号都是相当于在这个变量本身的基础上来进行运算 - 除了
=
之外,其他的符号都要求这个变量必须有值 - java中不支持连等定义,如(int i=j=5);但支持连等运算,如(int i = 5;i -= i *= i++;结果为-20)
- 都可以byte/short/char类型参与运算,运算之后结果类型不变
- 在运算的时候是从左往右编译的,但是在计算结果的时候从右往左计算
- int i = 5;i = i++; 结果为5,通过远算符优先级来解释,因自增运算符的优先级要
高于
赋值运算符。
3.3 关系运算符
- 运算的结果一定是逻辑值(true/false)
3.4 逻辑运算符
主要针对逻辑值
进行运算的
&
: 两边为true,则为true;遇false,则为false|
: 两边为false,则为false;遇true,则为true!
: 非真即假;非假即真^
: 两边相同则为假,两边不同则为真&&
: 运算规则和&
是一致的,如果前边的表达式的结果是false,则后边的表达式就不在运算了,短路,再考虑使用&&
时,两边要有交集才可。||
: 运算规则和|
是一致的,如果前边的表达式的结果是true,则后边的表达式就不在运算了,短路,使用||
时,两边有其一满足条件即可。||
在&&
前边的时候能够把&&
给短路掉,但是&&
在||
前边,不能把||
短路掉
//`||`在`&&`前边的时候能够把`&&`给短路掉
public class test {
public static void main(String[] args) {
int i = 3,j = 5;
boolean b = true || i++ > 1 && j++ > 3;
System.out.println(b); //true
System.out.println(i); //3
System.out.println(j); //5
}
}
//`&&`在`||`前边,不能把`||`短路掉
public class test {
public static void main(String[] args) {
int i = 3,j = 5;
boolean b = false && i++ > 1 || j++ > 3;
System.out.println(b); //true
System.out.println(i); //3
System.out.println(j); //6
}
}
3.5 位运算
注意
:位运算只针对整数的补码进行运算
&
、|
、^
- 交换值方式
-
- 交换值的三种方式的优劣性
-
-
- 亦或法:效率最高,使用频率低,只适用于整数值的交换。
-
-
-
- 加减法:效率低于亦或法,但又高于追尾法,理论上适用于数值型,但在小数上不准确,往往还是在整数上使用多。
-
-
-
- 追尾法:效率最低,使用频率高,适用于所有的类型
-
<<
、>>
、>>>
-
- 在进行移位运算,并不是直接移动对应的位数,而是将要移动的位数对32进行取余,移动的是余数对应的位数。如:28 << 35 = 28 << (35%32) = 28 << 3 = 224;右移为也类似。
~
:取反
3.6 三元运算符
- 逻辑值?表达式1:表达式2
- 执行顺序:先判断逻辑值,如果逻辑值为true,那么执行表达式1;反之执行表达式2
- 三元表达式依然是一个表达式,所以需要有一个计算结果,这个计算结果定义变量来进行存储。如:int i = 5,j = 8;i > j ? System.out.println(i):System.out.println(j);报错原因是没有结果可存储。
- 表达式1和表达式2的计算结果类型要一致或能兼容或能类型能转化
- 三元表达式在嵌套时,最好加上括号加以区分
public class test {
public static void main(String[] args) {
int i = 3,j = 8,k = 5;
int max = i > j ? (i > k ? i : k) : (j > k ? j : k);
System.out.println("最大值:" + max);
}
}
3.7 运算符优先级
()
~
++
--
!
*
/
%
+
-
<<
>>
>>>
关系
逻辑
&
|
三元运算符
赋值
:从左至右,优先级逐渐降低。
总结:一元运算 > 二元运算 > 三元运算 > 赋值
。
3.8 综合案例
- 定义一个整数变量,判断这个整数是一个奇数还是一个偶数
public class test {
public static void main(String[] args) {
/**
* 定义一个整数变量,判断这个整数是一个奇数还是一个偶数
*/
int num = 15;
//1&奇数为奇数,1&偶数为偶数,以此来判断奇偶数
String str = (num & 1) == 1 ? "奇数" : "偶数";
System.out.println(str);
}
}
- 定义一个变量表示分数,分数>=90-A、>=80-B、>=70-C、>=60-D、<60-E
public class test {
public static void main(String[] args) {
/**
* 定义一个变量表示分数,分数>=90-A、>=80-B、>=70-C、>=60-D、<60-E
*/
double score = 80;
//嵌套三元运算符,需要加括号加以区分
char level = score >= 90 ? 'A' : ((score >= 80) ? 'B' : ((score >= 70) ? 'C' : ((score >= 60) ? 'D' : 'E')));
System.out.println("分数对应的等级为:" + level);
}
}
- 定义一个变量表示年份,判断这一年是平年还是闰年
public class test {
public static void main(String[] args) {
/**
* 定义一个变量表示年份,判断这一年是平年还是闰年
*
* 分析:
* 闰年:逢百年整除400,不是百年整除4
* 平年:其他均为平年
* 如:2000为闰年(逢百年整除400)、2012为闰年(不是百年整除4)、2100为平年(逢百年不能整除400)
*/
int year = 1600;
//嵌套三元运算符
String str = year % 100 == 0 ? (year % 400 == 0 ? "闰年" : "平年") : ( year % 4 == 0 ? "闰年" : "平年");
System.out.println(str);
}
}
4、流程控制
4.1 顺序结构
指代码从上到下、从左到右来依次编译执行
4.2 分支结构
4.2.1 判断结构
对于判断结构适用于判断范围
,首先要对逻辑值
进行判断,只有逻辑值为true
,代码块
才会被执行。
if(逻辑值){
code;
}
- 执行顺序
-
- 如果逻辑值为true,那么就执行code,反之,不执行。
if(逻辑值){
code1;
}else{
code2;
}
- 执行顺序
-
- 先执行逻辑值,
-
- 如果逻辑值为true,那么就执行code1;
-
- 如果逻辑值为false,则执行code2。
if(逻辑值1){
code1;
}else if(逻辑值2){
code2;
}...
- 执行顺序
-
- 先执行逻辑值1,如果逻辑值1为true,,就执行code1;
-
- 如果逻辑值1为false,则会执行逻辑值2,如果逻辑值2为true ,执行code2。…
应用案例
- 输入三个整数,获取最大值
import java.util.Scanner;
public class test {
public static void main(String[] args) {
/**
* 输入三个整数,获取最大值
*/
Scanner s = new Scanner(System.in);
int i = s.nextInt();
int j = s.nextInt();
int k = s.nextInt();
//方法一:
int max = i;
if(max < j){
System.out.println("三个数中的最大值为:" + j);
}
if(max < k){
System.out.println("三个数中的最大值为:" + k);
}
//方法二:
if(i > j){
if(i > k){
System.out.println("三个数中的最大值为:" + i);
}else{
System.out.println("三个数中的最大值为:" + k);
}
}else{
if(j > k){
System.out.println("三个数中的最大值为:" + j);
}else{
System.out.println("三个数中的最大值为:" + k);
}
}
}
}
- 输入分数,获取分数的等级
import java.util.Scanner;
public class test {
public static void main(String[] args) {
/**
* 输入分数,获取分数的等级
*/
Scanner s = new Scanner(System.in);
double score = s.nextDouble();
//合法性的校验,要首先考虑
if(score > 100 || score < 0){
System.out.println("分数不合法");
}else if(score >= 90){
System.out.println("A");
}else if(score >= 80){
System.out.println("B");
}else if(score >= 70){
System.out.println("C");
}else if(score >= 60){
System.out.println("D");
}else
System.out.println("E"); //代码块只有`一句话`,那么`{}`可以省略不写
}
}
注意
:如果if或者是else中的代码块只有一句话
,那么{}
可以省略不写
4.2.2 选择结构
- 选项可把它理解为选择,是多个确定的数据供选择
- switch以及case中的选项:只能是byte/short/char/int,JDK1.7之后支持String,枚举
- 如果case之后没有break,那么从匹配的选项开始,然后依次往下执行,直到遇到break或者switch的末尾才结束。
- 打印每一个月有多少天
public class test {
public static void main(String[] args) {
/**
* 打印每一个月有多少天
*
* 案例:
* 31天的有哪几个月:1、3、5、7、8、10、12
* 30天的有哪几个月:4、6、9、11
* 如果是闰年:2月有29天
* 如果是平年:2月有28天
*/
int month = 4;
switch (month){
case 1:
case 3:
case 5:
case 7:
case 8:
case 10:
case 12:
System.out.println(month + " 月为31天");
break;
case 4:
case 6:
case 9:
case 11:
System.out.println(month + " 月为30天");
break;
case 2:
System.out.println(month + " 月为28天");
break;
}
}
}
- 如果每一个case之后都有break,case的顺序不影响结果
如果有一个或者多个case之后没有break,那么case会影响结果
switch(选项){
case 选项1:
code1;
break; //表示当前选项的结束
case 选项2:
code2;
break;
...
default: //如果其他选项都不匹配,则执行default的code;合法性校验,可写在此处
coden;
}
应用案例
- 输入三个数字分别表示年月日,计算这一天是这一年的第几天
import java.util.Scanner;
public class test {
public static void main(String[] args) {
/**
* 输入三个数字分别表示年月日,计算这一天是这一年的第几天
*
*/
Scanner s = new Scanner(System.in);
int year = s.nextInt();
int month = s.nextInt();
int day = s.nextInt();
//定义变量记录总得天数
int sum = 0;
//解题思路:借用没有break,它会依次往下累加,
switch(month){
case 12:sum += 30; //12月的时候,会经历完整的11月
case 11:sum += 31; //11月的时候,会经历完整的10月
case 10:sum += 30;
case 9:sum += 31;
case 8:sum += 31;
case 7:sum += 30;
case 6:sum += 31;
case 5:sum += 30;
case 4:sum += 31;
case 3:
//闰年和平年的判断,来确定2月的天数
if(year % 400 == 0 || year % 100 != 0 && year % 4 == 0){
//闰年29天
sum += 29;
}else{
//平年28天
sum += 28;
}
case 2:sum += 31;
case 1:sum += 0;
}
sum += day;
System.out.println(sum);
}
}
4.3 循环结构
4.3.1 while循环
需要定义变量来控制循环的次数,控制好循环执行的条件,控制次数的变量需要改变
while(逻辑值){
code;
}
- 执行流程
应用案例
- 输入一个整数n,打印1~n中所有的能被3整除,而不能被5整除的数字
import java.util.Scanner;
public class test {
public static void main(String[] args) {
/**
* 输入一个整数n,打印1~n中所有的能被3整除,而不能被5整除的数字
*
* 解题思路:
* 先获取1~n中所有3的倍数
* 再判断这个数是否能够被5整除
*/
Scanner s = new Scanner(System.in);
int n = s.nextInt();
int count = 3;
while(count <= n){
if(count % 5 != 0) System.out.println(count);
count += 3;
}
}
}
- 输入一个整数n,输出这个n是几位数
import java.util.Scanner;
public class test {
public static void main(String[] args) {
/**
* 输入一个整数n,输出这个n是几位数
*
*/
Scanner s = new Scanner(System.in);
int n = s.nextInt();
//定义一个变量来记录位数
int count = 0;
while(n != 0){
count++;
//整数除以10,直到被除等于0,位数就可以判断了,每除以一次10,就减少一位
n /= 10;
}
System.out.println(count);
}
}
- 输入一个整数n,打印这个整数的所有因数
import java.util.Scanner;
public class test {
public static void main(String[] args) {
/**
* 输入一个整数n,打印这个整数的所有因数
*
* 解题思路:
* 1、先获取1~n的所有数
* 2、在判断这个数能否整除n
*/
Scanner s = new Scanner(System.in);
int n = s.nextInt();
int count = 0;
while(count <= n){
if(n % count == 0) System.out.println(count);
count++;
}
}
}
4.3.2 do while循环
无论逻辑值是否满足,代码块至少执行一次,因代码块在循环条件之上,只要稍微改一下while循环条件,就能实现do while的效果。
do{
code; //代码块在逻辑值之上,所以不管循环条件是否满足都会执行一次
}while(逻辑值);
4.3.3 for循环
for(定义循环变量;控制条件;改变循环变量){
code;
}
总结
:
1、如果需要执行多次建议使用循环
2、如果次数固定或者变化比较规律,建议使用for循环;如果次数不固定或者变化没有规律,建议使用while循环。
应用案例
public class test {
public static void main(String[] args) {
/**
* *** 第n行
* **
* * 第1行
*
* 思路:
* 1、行数:n->1
* 2、第i行的*个数:i->1
*/
for (int i = 3; i > 0; i--) {
for (int j = i; j > 0; j--) {
System.out.print("*");
}
System.out.println();
}
/**
* * 第1行
* **
* *** 第n行
*
* 思路:
* 1、行数:1->n
* 2、第i行的空格个数:1->n-i
* 3、第i行的*个数:1->i
*/
for (int i = 1; i <= 3; i++) {
for(int j = 1;j <= 3 - i;j++){
System.out.print(" ");
}
for(int k = 1;k <= i;k++){
System.out.print("*");
}
System.out.println();
}
/**
* *** 第n行
* **
* * 第1行
*
* 思路:
* 1、行数:n->1
* 2、第i行的空格个数:n-i->1
* 3、第i行的*个数:i->1
*/
for (int i = 3; i > 0; i--) {
for(int j = 3 - i;j > 0;j--){
System.out.print(" ");
}
for(int k = i; k > 0;k--){
System.out.print("*");
}
System.out.println();
}
/**
* * 第1行
* ***
* *****
* ******* 第n行
*
* 思路:
* 行数:1->n
* 第i行空格数:1->n-i
* 第i行*数:1->2*i - 1
*/
for (int i = 1; i <= 4; i++) {
for(int j = 1; j <= 4 - i; j++){
System.out.print(" ");
}
for(int k = 1; k <= 2 * i - 1;k++){
System.out.print("*");
}
System.out.println();
}
}
}
public class test {
public static void main(String[] args) {
/**
* *
* **
* ***
* ****
* *****
*/
//方法一:
for (int i = 1; i <= 5; i++) {
for(int j = 1;j <= i;j++){
System.out.print("*");
}
System.out.println();
}
//方法二:
for(int i = 1,j = 1;i <= 5;j++){
//打印*
System.out.print("*");
//判断这一行是否到达了末尾
if(j == i){
//如果到达末尾,换行
System.out.println();
//新的一行,行数+1
i++;
j = 0;
}
}
}
}
4.3.4 break和continue
break
:仅在选择结构和循环结构中是使用,表示终止
当前的一层结构
continue
:只能用于循环结构中。表示跳过当前循环继续下次循环。
应用案例
- 输入一个数字,判断这个数字是否是一个质数
import java.util.Scanner;
public class test {
public static void main(String[] args) {
/**
* 输入一个数字,判断这个数字是否是一个质数
*
* 分析:
* 1、质数->只能被1和自身整除,1不是质数
* 2、n -> 只需从2~n-1判断即可,所以判断的次数就为:n-2
* 3、以因数为例:因数是能整除的数(如:6 :1、2、3、6,规律:只要过了一半的数都
* 未能整除,但要除了自身的数,则就没有能整除的数)
* 4、由此质数的判断次数则会变为:2~n/2(除了1和自身的数,只要判断n/2之间的数就可以了)
*/
Scanner s = new Scanner(System.in);
int num = s.nextInt();
//合法性校验
if(num <= 1){
System.out.println(num + " 不是质数");
}else{
//数字是质数,为true;不是质数,则为false
boolean flag = true;
for(int i = 2; i <= num/2;i++){
if(num % i == 0){
flag = false;
break;
}
}
if(flag) System.out.println(num + " 是质数");
else System.out.println(num + " 不是质数");
}
}
}
- 100文钱买100只鸡,3文买一只公鸡,2文买一只母鸡,1文买3只小鸡
public class test {
public static void main(String[] args) {
/**
* 百钱百鸡:100文钱买100只鸡,3文买一只公鸡,2文买一只母鸡,1文买3只小鸡
*
* 分析:
* 1、3文买一只公鸡,那么100文能买33只公鸡(gj)
* 2、2文买一只母鸡,那么100文能买50只母鸡(mj)
* 3、1文买3只小鸡,那么100文能买300只小鸡(xj)
*/
//方法一:
for(int gj = 0;gj < 33;gj++){
for(int mj = 0;mj < 50;mj++){
for(int xj = 0;xj < 100;xj++){
if(xj % 3 == 0){
if((gj + mj + xj) == 100 && (3 * gj + 2 * mj + xj * 1/3) == 100){
System.out.println("公鸡数量:" + gj + "母鸡数量:" + mj + "小鸡数量:" + xj);
}
}
}
}
}
//方法二:
for(int gj = 0;gj < 33;gj++){
for(int mj = 0;mj < 50;mj++){
int xj = 100 - gj -mj;
//xj的数量必须要满足能被3整除
if(xj % 3 == 0 && (3 * gj + 2 * mj + xj/3) == 100){
System.out.println("公鸡数量:" + gj + ",母鸡数量:" + mj + ",小鸡数量:" + xj);
}
}
}
}
}
- 输入一个数字n,然后将n分解质因数
import java.util.Scanner;
public class test {
public static void main(String[] args) {
/**
* 输入一个数字n,然后将n分解质因数
*
* 分析:
* 1、要从最小质数2开始循环
* 2、分解数字n,得到n被整除的质数,要求上一个质数必须除尽,才能进行下一个质数整除。
* 例如:100从最小质数2开始整除 得50,还能被2整除,得25,分解的数不能被2整除,
* 说明2被除尽了;
* 然后下一个最小质数3开始整除,发现不能整除;
* 在进行下一个数4整数,但4不是质数;
* 所以再进行下一个质数5开始整除,得5,分解的数还能被5整除,得1,
* 分解的数不能再被5整除,说明5被除尽了。
*/
Scanner s = new Scanner(System.in);
int n = s.nextInt();
//循环条件和控制次数可先不写,根据计算的实际情况来填写
for(int i = 2;n != 1;){
if(n % i == 0){
System.out.println(i);
/**
* 整除后将值重新赋给n,如此能继续寻找下一个质数,
* 直到n被整除的结果为1,循环结束,
* 此时把n!=1作为for循环的循环条件
*/
n /= i;
}else{
//上一个质数i已经除尽了,需要自增i++
i++;
}
}
}
}
5、一维数组
- 存储同一类型的多个数据
容器
- 大小固定
- 对存入的数组元素进行了
编号
,这个编号称为下标也称索引,是从0开始算的。
5.1 定义数组
数组5.1.1
、5.1.2
的声明和初始化是能分开的,5.1.3
的声明和初始化不能分开
5.1.1 动态初始化
只知道大小,不知道具体元素
数据类型[] 数组名 = new 数组类型[长度];
例
:int[] arr = new int[5];//表示定义了一个能存储5个整型元素的数组
5.1.2 静态初始化①
数组类型[] 数组名 = new 数据类型[]{
元素1,元素2,...};//数组的长度是确定的,长度就不能变了
例
:int[] arr = new int[]{1,2,3,4,5,6};
5.1.3 静态初始化②
数组类型[] 数组名 = {
元素1,元素2,...};
例:int[] arr = {1,2,3,4,5,6};
5.1.4 内存
-
方法实参传递给形参的时候一定足以:一切都是值传递
-
如果是基本数据类型,那么传递的就是字面值
-
如果是引用数据类型,那么传递的就是地址值
-
Java将内存分为了5块:
栈内存
、堆内存
、方法区
、本地方法栈
、PC计数器
(寄存器)
A 、栈内存:存储变量。变量在声明的时候存储到栈内存中,不会自动给值,除非程序中手动给值。变量在栈内存中使用完成
之后要立即释放
。
B、堆内存:存储的是对象。对象在存储到堆内存中之后,会被堆内存赋予一个默认值。对象
使用完成不一定
会从堆内存中立即移除
,而是在不确定
的某个时刻被回收
- byte/short/int:默认值为
0
- long:默认值为
0L
- float:默认值为
0.0F
- double:默认值为
0.0
- char:默认值为
\u000
;用的是utf-16来存储的,\u000是空字符 - boolean:默认值为
false
- 引用类型数据:数组、String、类,默认值为
null
- byte/short/int:默认值为
5.2 遍历数组
5.2.1 普通for循环:先获取下标
,然后利用下标获取
元素
备注
:可循环,也可改变
数组元素
for(int i = 0;i < arr.length;i++){
//通过下标获取数组元素
System.out.println(arr[i]);
}
5.2.2 增强for循环:定义变量
来依次表示每一个元素
注意
:只能遍历数组而不能
改变数组中元素
for(int i:arr){
//由于数组的连续性,不依赖下标直接获取元素
System.out.println(i);
}
5.2.3 数组的元素拼接
成字符串返回
String str = Arrays.toString(arr);
System.out.println(str);
5.2.4 数组的排序
冒泡
、选择
排序:可升序也可降序,时间复杂度:O(n^2)Arrays.sort(arr)
:只能升序排序,底层是以快速排序+归并排序,时间复杂度:O(nlogn)
应用案例
- 获取数组中的最值
public class test {
public static void main(String[] args) {
/**
* 获取数组中的最大值
*/
int[] arr = {
2,8,9,2,7,3,0,8,3,1,4};
//方法一:
int max1 = arr[0]; //定义最大值
for(int i = 0;i < arr.length;i++){
if(arr[i] > max1) max1 = arr[i];
}
System.out.println("数组中的最大值为:" + max1);
//方法二:
int max2 = 0; //定义最大值的索引
for(int i = 0;i < arr.length;i++){
if(arr[i] > arr[max2]) max2 = i;
}
System.out.println("通过下标获取最大值:" + arr[max2]);
}
}
- 查询指定元素的位置
public class Test5 {
public static void main(String[] args) {
/**
* 查询指定元素的位置
*/
int[] arr = {
12,34,56,7,3,10};
//调用方法
int index = getIndex(arr,56);
if(index != -1)
System.out.println("元素对应的索引:" + index);
else //index = -1
System.out.println("没有该元素!");
}
/**
* 定义一个方法:查询数组中指定的元素对应的索引
* 不确定因素:哪个数组,哪个指定元素(形参)
* 返回值:索引
*/
public static int getIndex(int[] arr,int ele){
int index = -1;//这个初始值只要不是数组的索引即可
for (int i = 0; i < arr.length; i++) {
if(arr[i] == ele){
index = i;//只要找到了元素,那么index就变成为1
break;//只要找到这个元素,循环就停止
}
}
return index;
}
}
- 添加元素
import java.util.Scanner;
public class Test5 {
public static void main(String[] args) {
/**
* 添加元素
*/
int[] arr = {
12,34,56,7,3,10};
//输出增加元素前的数组
System.out.print("增加元素前的数组:");
for (int i = 0; i < arr.length; i++) {
if(i != arr.length - 1 )
System.out.print(arr[i] + ",");
else
System.out.println(arr[i]);
}
//从键盘接收数据
Scanner sc = new Scanner(System.in);
System.out.print("请录入你要添加元素的指定下标:");
int index = sc.nextInt();
System.out.print("请录入你要添加的元素:");
int ele = sc.nextInt();
//增加元素
//调用方法
insertEle(arr,index,ele);
/**
* arr[5]=arr[4]
* arr[4]=arr[3]
* arr[3]=arr[2]
*
* 有规律的重复,可用循环解决
*/
// int index = 1;
// for(int i = arr.length - 1; i >= (index + 1);i--){
// arr[i] = arr[i - 1];
// }
// arr[index] = 666;
//输出增加元素后的数组
System.out.print("增加元素后的数组:");
for (int i = 0; i < arr.length; i++) {
if(i != arr.length - 1 )
System.out.print(arr[i] + ",");
else
System.out.println(arr[i]);
}
}
/**
* 提取一個添加元素的方法:
* 在数组的指定位置上添加一个指定的元素
* 在哪个数组的哪个位置添加哪个元素
* 不确定因素:形参;哪个数组;哪个位置;哪个元素
* 返回值:无
* @param arr
*/
public static void insertEle(int[] arr,int index,int ele){
for(int i = arr.length - 1; i >= (index + 1);i--){
arr[i] = arr[i - 1];
}
arr[index] = ele;
}
}
- 删除指定元素
import java.util.Arrays;
public class Test5 {
public static void main(String[] args) {
/**
* 添加元素
*/
int[] arr = {
12,34,56,7,3,10};
//输出删除元素前的数组
System.out.print("删除元素前的数组:");
System.out.println(Arrays.toString(arr));
//找到要删除的元素对应的索引即可:
int index = -1;
for(int i = 0;i < arr.length;i++){
if(arr[i] == 3){
index = i;
break;
}
}
//删除元素
//调用方法
/**
* arr[2]=arr[3]
* arr[3]=arr[4]
* arr[4]=arr[5]
* arr[5]=0
*
* 有规律的重复,可用循环解决
*/
if(index != -1){
for(int i = index;i <= arr.length - index;i++){
arr[i] = arr[i + 1];
}
arr[arr.length - 1] = 0;
}else{
System.out.println("没有你要删除的元素!!!");
}
//输出增加元素后的数组
System.out.print("增加元素后的数组:");
System.out.println(Arrays.toString(arr));
}
}
- 数组冒泡排序
import java.util.Arrays;
public class test {
public static void main(String[] args) {
/**
* 数组冒泡排序
*
* 分析:
* 1、冒泡排序的轮次:arr.length - 1,可确定外循环需要循环的次数
* 2、冒泡排序过程中,第i轮需比较元素的次数:arr.length - i,可确定内循环需要循环的次数
*/
int[] arr = {
2,8,9,2,7,3,0,8,3,1,4};
for(int i = 1;i < arr.length;i++){
for(int j = 1;j < arr.length - i + 1;j++){
if(arr[j - 1] > arr[j]){
//追尾法实现值交换
int temp = arr[j - 1];
arr[j - 1] = arr[j];
arr[j] = temp;
}
}
}
//打印排序后的数组
System.out.println(Arrays.toString(arr));
}
}
扩展
:
- 时间复杂度
A、时间复杂度:找重复执行的这段代码,将这段代码的执行时间认为是单位1,那么执行这个单位1的次数就是时间复杂度(用O()表示)----习惯上,只考虑最高阶,不考虑系数
B、冒泡排序的时间复杂度:O(n^2)
C、时间复杂度符合形式:n^x
,(logn)^x
,n^x(logn)^y
- 空间复杂度
A、在已知条件下,执行这段代码需要额外耗费的空间数量- 以冒泡为例解释:数组长度随意变化的情况下,冒泡排序只需要3个变量,不随长度变化而变化。3 = 3 * n ^0 ->n ^0------空间复杂度为:O(1)
注意
:时间复杂度不决定时间的长短,决定的是次数的多少。
- 数组选择排序
import java.util.Arrays;
public class test {
public static void main(String[] args) {
/**
* 数组选择排序
*
* 分析:
* 1、选择排序的轮次:arr.length - 1,可确定外循环需要循环的次数
* 2、选择排序过程中,第i轮选择的下标:i - 1
* 3、要比较的下标:i -> arr.length - 1(控制要比较的下标)
*/
int[] arr = {
2,8,9,2,7,3,0,8,3,1,4};
//控制轮数
for(int i = 1;i < arr.length;i++){
//控制要比较的下标
for(int j = i; j < arr.length;j++){
//判断选择的下标与要比较的下标数组元素大小
if(arr[i - 1] > arr[j]){
//交换值
int temp = arr[i - 1];
arr[i - 1] = arr[j];
arr[j] = temp;
}
}
}
//打印排序的结果
System.out.println(Arrays.toString(arr));
}
}
扩展`:
-
时间复杂度
A、选择排序的时间复杂度:O(n^2) -
空间复杂度
A、选择排序的空间复杂度为:O(1)
5.2.5 数组的反转
- 传统方法:创建新数组,从原数组倒着拿,往新数组正着放,时间复杂度:O(n);空间复杂度:O(n)
首尾交换法
:时间复杂度:O(n);空间复杂度:O(1)
应用案例
- 数组反转
import java.util.Arrays;
public class test {
public static void main(String[] args) {
/**
* 数组的反转
*
* 分析:
* 1、首尾交换法实现
*/
int[] arr = {
2,8,9,2,7,3,0,8,3,1,4};
for(int i = 0,j = arr.length - 1;i < j;i++,j--){
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
//打印排序的结果
System.out.println(Arrays.toString(arr));
}
}
5.2.6 数组元素的查找
- 数组无序的情况,逐个遍历比较
应用案例
public class test {
public static void main(String[] args) {
/**
* 数组无序的情况下,二分法查找数组元素的索引
*
*/
int[] arr = {
2,8,9,2,7,3,0,8,3,1,4};
int num = 9;
for(int i = 0;i < arr.length;i++){
if(arr[i] == num){
System.out.println("数组中的 " + num + " 所在的索引为:" + i);
}
}
}
}
- 数组有序的情况,使用二分法查找,时间复杂度:
O(logn)
;空间复杂度:O(1)
应用案例
public class test {
public static void main(String[] args) {
/**
* 数组有序的情况下,查找数组元素的索引
*
*/
int[] arr = {
14,26,37,58,64,68,82,90};
int num = 58;
//记录最小值的下标
int min = 0;
//记录最大值的下标
int max = arr.length - 1;
//计算中间值的下标
int mid = (min + max)/2;
//方式一:
while(arr[mid] != num){
if (num > arr[mid]) min = mid + 1;
else max = mid - 1;
if(min > max){
mid = -1;
break;
}
mid = (min + max)/2;
}
System.out.println("数组中的 " + num + " 所在的索引为:" + mid);
//方法二:
while(min <= max){
if(num == arr[mid]) break;
else if(num > arr[mid]) min = mid + 1;
else max = mid - 1;
mid = (min + max)/2;
}
System.out.println("数组中的 " + num + " 所在的索引为:" + mid);
}
}
5.2.7 数组的复制
- 数组复制
System.arraycopy(源数组,复制源数组元素的起始下标,目标数组,目标数组的起始下标,要复制的长度);
- 数组扩容
本质上是在做数组的复制
过程,复制完成之后一定是产生了一个新
的数组
Arrays.copyOf(源数组,改变源数组长度);
应用案例
import java.util.Arrays;
public class test {
public static void main(String[] args) {
int[] arr1 = {
5,6,9,11,6,3,8,4,18};
int[] arr2 = new int[5];
/**
* 数组的复制
*/
System.arraycopy(arr1,3,arr2,1,3);
for (int i : arr2) {
System.out.println(i);
}
/**
* 数组的扩容
*
* 实际上数组的扩容本质上就是数组的复制过程
* System.arraycopy(arr1,0,newArr,0,arr1.length); //数组的复制
* arr1 = newArr; //把复制的新数组的内存地址赋给arr1
*
* ============================================
* 底层对数组扩容时,有做数组长度的判断,扩容的最小数组长度遵循小者优先
* int[] newArr = new int[len];
* int min = arr1.length > len ? len : arr1.length;
*/
/**
* arr1.length < len,因此新数组的长度为arr1.length,
* 扩容后数组长度为arr1.length;
* 此种情况:扩容
*/
arr1 = Arrays.copyOf(arr1,15);
/**
* arr1.length > len ,因此新数组的长度为len,
* 扩容后数组长度为5
* 此种情况:缩容
*/
// arr1 = Arrays.copyOf(arr1,5);
}
}
5.2.8 main方法
5.2.9 可变参数
- JDK1.5出现的新特性
- 作用提供一个方法,参数的个数是可变的
- 解决了部分方法的重载问题
- 方法的内部对可变参数的处理跟数组是一样的
- 可变参数和其他数据一起作为形参的时候,可变参数一定要放在最后
public class Test5 {
/**
* 可变参数:作用提供一个方法,参数的个数是可变的
* int... num
* double... num
* 作用:解决了部分方法的重载问题
* @param args
*/
public static void main(String[] args) {
method01(10);
method01();
method01(20,30,40);
method01(new int[]{
11,2,33,44});
}
public static void method01(int... num){
System.out.println("------------1");
for (int i : num) {
System.out.print(i + "\t");
}
System.out.println();
}
}
5.2.10 Arrays
import java.util.Arrays;
public class Test5 {
public static void main(String[] args) {
//给定一个数组
int[] arr = {
1,3,7,2,4,8};
//toString:对数组进行遍历查看的,返回的是一个字符串,这个字符串比较好看
System.out.println(Arrays.toString(arr));
//binarySearch:二分法查找,使用这个方法前提:一定是一个有序的数组
Arrays.sort(arr);
System.out.println(Arrays.binarySearch(arr,4));
//copyOf:完成数组的复制
int[] arr2 = {
1,3,7,2,4,8};
int[] newArr = Arrays.copyOf(arr2,4);
System.out.println(Arrays.toString(newArr));
//copyOfRange:区间复制
int[] newArr2 = Arrays.copyOfRange(arr2,1,4); //[1,4)--->1~3位置
System.out.println(Arrays.toString(newArr2));