Hello World
环境变量
JAVA_HOME
C:\Program Files\Java\jdk1.8.0_291
Path
;C:\Program Files\Java\jdk1.8.0_291\bin;
CLASS_PATH
C:\Program Files\Java\jdk1.8.0_291\lib;
概念
- JDK(Java Development Kit) — Java开发工具包 — JRE+开发工具
开发java程序最小的环境为JDK,所以JDK是JAVA语言的核心 - JRE(Java Runtime Environment) — Java运行时环境 — JVM+运行java程序所必需的环境
运行java程序最小的环境为JRE - JVM(Java Virtual Machine)—负责加载.class并运行.class文件
JVM(JAVA虚拟机)将JAVA代码转换为对应的操作系统可以理解的指令,不同的操作系统有不同虚拟机与之对应,同一段代码交给虚拟机之后,虚拟机再转化给操作系统
Java语言平台版本
- J2SE/JAVASE — Standard Edition—标准版/基础版
开发普通桌面和商务应用程序提供的解决方案,该技术体系是其他两者的基础。 - J2EE/JAVAEE — Enterprise Edition— 企业版/商务版
是为开发企业环境下的应用程序提供的一套解决方案,主要针对于企业级应用程序开发。 - J2ME/JAVAME — Micro Edition — 微型版/移动版
是为开发移动设备和嵌入式设备(电器、机器人、机顶盒…)提供的解决方案。Java不是不侧重,而是分支出安卓。
Java语言是跨平台的,但是支撑Java运行的JVM是不跨平台的
类文件结构
- package:全小写,域名倒写,单词之间用点隔开
语法基础
标识符
- 可以由字母、数字、下划线和美元符组成
- 严格区分大小写,且不能以数字开头
- 不允许使用Java的关键字(关键字全都是小写的),其中const和goto是保留字
- 标识符的命名最好是能见名知意,不怕长
- true、false、null三个也不可以做标识符
- 驼峰命名法:一般类名是大驼峰,方法名是小驼峰。大驼峰是指从第一个单词的首字母就大写,小驼峰是指从第二个单词开始首字母大写。
注释
/**
* 文档注释,可以写多行,可以添加@author @since 等参数
*
* @author Administrator
*
*/
/*
* 我是一个
* 多行注释
*/
// 我是一个单行注释
变量
程序中需要记录一些会变化的数据,这种数据就叫作变量
变量来源于数学,是计算机语言中能储存计算结果或能表示值的抽象概念。参考地址
局部变量:
- 生命周期:从
当前位置
开始到当前位置之前的最近的“{”所对应当前位置后面的“}”
结束,也就是方法里或是局部代码块中,方法运行完内存就释放了。 - 位置:定义在方法里或是局部代码块中的。
- 注意:必须手动初始化来分配内存–给变量赋值
成员变量
- 位置:类里面、方法外面
- 注意:初始化不是必须,会有对应的默认值
- 作用域:在整个类中都生效,类消失后成员变量才会消失
以下是各类型的默认值
byte:0
short:0
int:0
long:0 // 整数类型的默认值都是0
float:0.0
double:0.0 // 浮点类型的默认值都是0.0
char: // 这里不是空格,是unicode里面0对应的那个字符
boolean:false // 布尔的默认就是false
String:null // 引用类型的默认值都是null
定义格式:
int age = 18;
int a, b, c;
a = 1;
b = 2;
c = 3;
int d = 4, e = 5, f = 6;
常量
常量是指在程序的整个运行过程中值保持不变的量。在这里要注意常量和常量值是不同的概念,常量值是常量的具体和直观的表现形式,常量是形式化的表现。通常在程序中既可以直接使用常量值,也可以使用常量。参考地址
方法
- 定义:权限修饰符 返回值类型 方法名(参数列表){方法体}
- 调用:通过方法名+参数列表的方式来确定具体调用哪个方法
- 重载:在同一个类中存在多个方法名相同,但参数列表不同的方法。如果在同类中,同名方法的参数个数不同,一定构成重载。如果同类中,同名方法的参数个数相同,需要查看对应位置上参数的类型,而不是参数名,与参数名无关。
- 关于重载方法的参数列表匹配的问题
- 对于引用类型,如果有精确匹配的,则绑定精确匹配的,否则绑定形参是最近父类的方法
- 对于基本数据类型,则优先绑定精确匹配的,再自动类型提升(从小范围到大范围提升,byte->short->int->long->float->double),再装箱成引用类型后参考第1条。例如:long可以接收int(自动类型提升),但是Long不能接收int(引用类型无自动类型提升)此条参考
public static void method() {
System.out.println("无参Method");
}
// 与第一个方法同名,但多了一个参数,构成重载
public static void method(int i) {
System.out.println("整数类型Method");
}
// 与前两个方法同名,但又多了一个参数,构成重载
public static void method(int n, String name) {
System.out.println("n:" + n + " name:" + name);
}
// 与前三个方法同名,且与第三个方法参数个数相同,但是对应位置上的参数类型不完全相同,因此也构成重载
public static void method(String name, int n) {
System.out.println(name + " today will eat " + n + " food");
}
数据类型
基本类型
- 关于取值范围
byte(Byte)、short(Short)、int(Integer)、long(Long)、float(Float)、double(Double)、char(Character)这七种基本类型的包装类里面都有一对常量,分别是MIN_VALUE和MAX_VALUE用来记录当前类型的取值范围 - char类型
Literals of types char and String may contain any Unicode (UTF-16) characters. If your editor and file system allow it, you can use such characters directly in your code.官方文档
只能保存一个字符,需要用单引号包裹,如
char a = '0';//存的是字符 0 ,对应的编码是48
char c = 'A';//存的是字符 A ,对应的编码是65
char d = '中';//存的是字符 中 ,对应的编码是20013
char e = '7';//存的是字符 7 ,对应的编码是55
char f = 97;//存的是数字,会认为是编码97,对应的字符是a
char g = '\u004e';//反斜线加U后面跟的是四位16进制值,1位16进制可以记录4bit,4位正好16bit也就是2字节,此例中的4e转成10进制4*16+14=78,即编码值,对应的字符是 N。另整数。0~65535。十进制、八进制、十六进制均可。输出字符编码表中对应的字符。
char m = 'a' + 'b';//char类型相加,编译器自动优化为两个字符的编码的值相加后的值赋值给m
char m = 'a' + 1;//输出结果是b。编译器自动优化为字符a的编码+1后的值赋值给m
int b = 1;char m = 'a' + b;//会报错。因为b是整数类型,两数相加会自动提升成int类型,
int m = 'a' + 'b';//结果是195。//195没有超出int范围,直接输出195。
可以根据情况和数字互相转换,如char c1 = 97;int a = c1 + 0;
Java用的是Unicode。不过8位的ASCII码包含在Unicode中,是从0~127的
字面值
- 整数字面值是int类型
- 浮点字面值是double类型
- byte、short、char三种比int取值范围小的类型可以使用范围内的值直接赋值;
- 字面值的后缀:L-long、F-float、D-double
- 字面值的前缀:0b 二进制、0 八进制、0x 十六进制
类型转换
小到大,直接转。大到小,强制转。浮变整,小数没
byte a = 10;
char c = (char)b;//不是相同的类型,所以需要强转
short b = a;//不会报错,小转大
int c = 1;
long d = c;// 小转大
float f = 3.14159f;
double g = f;//小转大
byte h = (byte)128;//超过byte的范围后就被看作是int了
short i = (short)32768;//超过short范围后也是被看作int
运算规则
- 两不同类型运算,结果与两类型中范围较大的一方保持一致
- byte、short、char运算时会先自动提升成int再运算
- 整数运算可能会出现溢出的问题,注意提前使用L后缀提升成Long值再算
- 浮点数运算不精确(1-0.8)
- 浮点数的特殊值:Infinity(浮点数/0)、NaN(0/0.0)。
- 对于被除数为0或是0.0的运算中,当结果是整数时,都会报除0错误;当结果是浮点数时,被除数是0或是0.0的结果都是NaN,除此之外其他的都是Infinity,正负根据正常运算规则来确定
除数/被除数 | 0 | 0.0 |
---|---|---|
正整数 | / by zero | Infinity |
0 | / by zero | NaN |
负整数 | / by zero | -Infinify |
正浮点数 | Infinity | Infinity |
0.0 | NaN | NaN |
负浮点数 | -Infinity | -Infinity |
- 两相同类型运算,结果还是此类型
运算符
运算顺序
Operator | Precedence |
---|---|
postfix | expr++ expr-- |
unary | ++expr --expr +expr -expr ~ ! |
multiplicative | * / % |
additive | + - |
shift | << >> >>> |
relational | < > <= >= instanceof |
equality | == != |
bitwise AND | & |
bitwise exclusive OR | ^ |
bitwise inclusive OR | | |
logical AND | && |
logical OR | || |
ternary | ? : |
assignment | = += -= *= /= %= &= ^= ` |
除赋值运算符外,所有二元运算符都是从左到右求值的; 赋值操作符是从右向左求值的。 |
https://docs.oracle.com/javase/tutorial/java/nutsandbolts/operators.html
// 助记词 : 单 算 移 关 位 逻 三
单目>算数运算符>移位>比较>按位>逻辑>三目>赋值
单目运算符(一元运算符,即一个变量):+,-,++,--
算数运算符:+,-,*,/,%
移位运算符:<<,>>
关系运算符:>,<,>=,<=,==,!=
位运算符:&,|,~,^,
逻辑运算符:&&,||
三目运算符:表达式1?表达式2:表达式3;
赋值运算符:=等
+=
、-=
、*=
、/=
、%=
运算符会自动做类型强转,所以不会报编译错误,如下为官方解释
- 比较运算符的结果都是布尔类型(
== != < <= > >=
) ==
比较的是左右两边的值,如果是基本型,比较的是字面值。如果是引用类型,比较的是两个类变量所保存的对象的地址值。对于基本类型的包装类进行比较时,如果没有遇到运算是不会触发自动拆箱的。
Integer a = 1;
Integer b = 127;
Integer c = 128;
Integer d = 128;
// == 在有运算时会自动拆箱
System.out.println(c == (a + b));//true有运算,拆
System.out.println(c == d);//false不拆
System.out.println(c == 128);//true拆
System.out.println(d == 128);//true拆
-
- 如果基本数据类型与包装类型使用
==
做比较时,会自动拆箱
- 如果基本数据类型与包装类型使用
public static void main(String args[])
{
int a = getCode();
Integer b = new Integer(1);
System.out.println(a == b);
}
public static int getCode(){
return 1;
}
以上代码反编译后如下
public static void main(String[] paramArrayOfString) {
int i = getCode();
Integer integer = new Integer(1);
System.out.println((i == integer.intValue())); // 自动拆箱
}
public static int getCode() {
return 1;
}
NaN != NaN 结果为true //特殊情况
- ++ 和 – 写在后边是先取值再自操作,写在前边是先自操作再取值
使用前缀的形式时,是先自操作(自增或是自减),然后再取整个表达式的值。使用后缀的形式时,是先取变量的值,再对变量进行操作。++和–不会触发自动类型提升,比如byte a = 0;byte b = a++;
是可以的
int a = 0;
int b = a++ + ++a + a;
//第一个a++,表达式的值为0,然后a自增变成1
//第二个是++a,此时a的值已经是1了,使用的前缀式,所以先自增1变成2,再取表达式的值2,至此表达式的值是0+2=2了
//第三个a没有进行任何操作所以直接取a的值2,整个表达式的值就是0+2+2=4,所以b=4,a=2
- && 和 || 的短路不是任何情况下都会发生,只有前半部是false(&&)和true(||)这两种情况下才会发生短路
测试.
一般进行逻辑运算的时候是使用双与、双或
int a = 0;
if( (a++) > -1 || (a--) < 0) {//此时发生短路
}
System.out.println(a);//1
int a = 0;
if( (a++) > 0 || (a--) < 0) {//不发生短路
}
System.out.println(a);//0
- 三目运算符
exp1 ? exp2 : exp3;
根据exp1的true或false来决定是执行exp2还是exp3,可以嵌套。当冒号两边的表达式可以进行自动类型转换时,将执行此转换.
例:((x>4)?99.9 :9) 返回值为99.9或9.0 instanceof
运算符
用于判断某对象是否为类的实例、子类的实例,还是实现特定接口的类的实例
(对象) instanceof (某个类或接口,直接写类名或接口名字即可)
null
对任何类型使用instanceof
判断都返回false
流程控制
顺序结构
顺序结构从头到尾所有代码依次都会执行到,可以解决输入、输出、计算等问题,但是不可以先做判断,再选择性的执行代码
分支结构
if
条件判断
- 格式:
//单分支,适合只有一个判断条件时使用,符合条件执行,不符合就跳过相关代码
if (判断条件) {
条件为真时执行代码
}
//多分支,适合有两种情况时使用,符合条件执行代码1,其他条件执行代码2
if (判断条件) {
条件为真时执行代码1
} else {
条件不成立时执行代码2
}
//嵌套分支,适合有多种情况时使用,else if的个数没有限制,最后的else也是可选项
if (判断条件1) {
条件1为真时执行代码
} else if (判断条件2) {
条件2不成立时执行代码
} else if (判断条件3) {
条件3不成立时执行代码
} else if (判断条件4) {
条件4为真时执行代码
} else {
以上条件都不满足时执行代码
}
对于取值范围判断的问题:
- 如果判断范围是连续:在使用if进行条件判断时可以沿着一个方向(从小到大或是从大到小的方向)来判断,每个if里面的条件只要写小于当前范围的最大值或是大于当前范围的最大值就可以了。
- 如果判断范围不连续:此种情况下if里面的条件就只能写完整的限定条件了。
if (bmi < 18.5) {
result = "过轻";
} else if (bmi <= 22.9) {
result = "正常";
} else if (bmi <= 24.9) {
result = "偏胖";
} else if (bmi <= 29.9) {
result = "肥胖";
} else if (bmi <= 40) {
result = "重度肥胖";
} else {
result = "极度肥胖";
}
switch选择结构
格式
//select可以是byte、short、int、char及其包装类,还有String和Enum
switch(select){
case v1: code1;break;//v1的格式要与select保持一致
case v2: code2;break;
case v3: code3;break;
default: break;
}
- 执行顺序:会拿着变量a的值依次与每个case后面的值做比较,如果匹配则执行case后的语句,只要没有遇到break;会一直向后执行所有的case,包括default,此种现象称作穿透现象。
- 如果设置了default,则即使没有任何匹配项的时候也会执行default里面的语句
- default 和 break 都是可选项
循环结构
for循环
格式
for (code1; code2; code3) {
code4;
}
//执行顺序:code1 => [code2 => code4 => code3] 中括号里面是重复执行的内容
int a[] = {1, 3};
for (int v : a) {
code;
}
- 注意:循环能执行多少次与循环变量的取值范围没有直接的关系,只取决于循环变量实际可以取到几个值
for (int i = 8; i <= 8888; i = i * 10 + 8){
//这里的i取值范围是[8,8888],包含了8880个值
//但是根据步进条件,`i = i * 10 + 8`
//可知在这个范围里面实际只能取到四个值,因此循环只执行四次
}
for循环书写需求分析
- 循环变量:变量要代表的是什么
- 循环开始条件:变量从哪里开始
- 循环终止条件:变量到哪里结束
- 步进条件:循环变量下一个值如何根据上一个值推导出来
嵌套for循环
格式
外层循环执行一次,内层循环执行多次(一轮)
// 外层循环控制图形的行数
for (int r = 0; r < 3; r++) {
// 内层循环控制图形的列数
for (int c = 0; c < 5; c++) {
System.out.print("*");
}
// 打印完成一行后,输出一个换行符
System.out.println();
}
// 打印99乘法表
for (int i = 1; i <= 9; i++) {
for (int j = 1; j <= i; j++) {
System.out.print(String.format("%d*%d=%d\t", j, i, j * i));
}
System.out.println();
}
增强for循环
- 好处:比普通for循环写法简单,而且效率高
- 坏处:没有办法按照下标来操作值,只能从头到尾依次遍历所有数据
for (String s : stringArray) {
System.out.println(s);
}
break和continue关键字
break定义
结束当前循环,并跳出当前所在最近一层的循环体 (最近的“{”的所圈定的循环体)
continue定义
跳过当前循环体中后面的语句,直接进入下一轮循环
- 注意:break和continue后面都不可以直接写代码,都是不可达的代码
Random r =new Random();
Scanner sc = new Scanner(System.in);
int count= 100,value = r.nextInt(count);
for(int i =0;i<count;i++) {
System.out.println("请输入你要猜测的数字:");
int n = sc.nextInt();
if(n !=value) {
System.out.println("猜错了");
continue;
}
System.out.println("我是用来检测continue有没有生效的");
// 为了演示才做了判断,实际不需要
if(n == value) {
System.out.println("猜对了");
break;
}
}
while循环
先判断再执行
int n = 10;
// 必须满足条件才会执行循环体,否则一次都不执行
while(n >=10) {
n= new Random().nextInt(100);
System.out.println(n);
}
do while循环
先执行一次循环体,再执行判断
int n;
do {
// 先执行循环体
n = new Random().nextInt(100);
System.out.println(n);
// 循环体结束后,执行下一次循环体之前做一个判断
} while (n > 50);
三种循环的区别
- for 知道循环次数时
- while/do while 当循环次数不确定的时候
- while 先判断,不符合规则的话一次都不会执行代码
- do while 代码最少执行一次,再去判断,符合规则 ,则再次执行代码
- 循环之间都可以互相替代,但是一般最选择合适的循环结构来完成代码
数组
定义
数组Array,保存多个相同类型的值的,通过数组的下标操作数组中的每一个元素。下标从0开始,晨大下标为数组的长度-1。
在C语言中,数组名是当作指针来处理的。更确切的说,数组名就是指向数组首元素地址的指针,数组索引就是距数组首元素地址的偏移量。
参考地址
数组也是引用类型
- 查看数组的长度,c1.length,是个属性
- 数组一旦创建,长度不可改变。如果想要增加/删除数组中的元素,只能先创建一个新长度的新数组,再将原来的数据复制过去
// 动态创建
int[] a = new int[5]; // a是一个int类型的数组,动态创建
//静态创建
int[] b = new int[]{1,2,3,4,5};
int[] c = {1,32,4,5,56};
数组工具包
- Arrays.copyOf(T[] original, int newLength)
原数组和新数组的长度,返回的是一个全新的数组,不是原数组。
newLength<length:缩容,子集
newLength== length:复制成副本
newLength>length:扩容,多出来的元素为类型默认值 - Arrays.toString(数组名);char 类型的数组底层中做了处理,可以直接打印数组中的具体元素,但除了char类型以外的数组,如果想要查看数组中的具体元素,需要使用Arrays.toString(数组名);注意Arrays需要导包
- public static native void arraycopy(Object src, int srcPos,
Object dest, int destPos,
int length);从一个数组向另一个数组拷贝数据
src – 源数组
srcPos – 源数组中需要被拷贝的起点位置
dest – 目标数组
destPos – 目标数组中要保存拷贝过来数据的起点位置
length – 需要拷贝的元素的数量
二维数组/多维数组
扩展:排序
冒泡排序
这里有个用动画形式来展示冒泡排序思想的,动画地址
思想:当前元素和后一个元素比较,如果当前元素小于后一个元素,则两者交换位置,如此操作一轮,则当前数组的最大值就被排到了最后一个位置上。然后进行第二轮,结果第二大的元素被移到了倒数第二的位置。然后…,直接所有的元素被排好
原地排序、稳定排序、O(n2)
public static void bubbleSort2(int[] a) {
for (int i = 0; i < a.length - 1; i++) {
for (int j = 0; j < a.length - 1 - i; j++) {
if(a[j]>a[j+1]) {
int t= a[j];
a[j]=a[j+1];
a[j+1]=t;
}
}
}
}
插入排序
思想:假设把数组分成两段,前一段是按升序排好了的,后面的元素需要从一头挨个比较,直到找到正确的位置插入就可以了,在此前数组中的数据是向后挪或是向前挪的,并不需要三次交换,因此时间上要优于冒泡排序。
public static void insertSort(int[] b) {
int count = 0;
for (int i = 1; i < b.length; i++) {
int value = b[i], j = i - 1;
for (; j >= 0; j--) {
count++;
if (value < b[j]) {
b[j + 1] = b[j];
} else {
break;
}
}
b[j + 1] = value;
}
}