Java基本语法
一、关键字与标识符
关键字
被Java语言赋予了特殊含义,用作专门用途的字符串(单词)
关键字中所有的字母都为小写
作用 | 关键字 |
---|---|
定义数据类型的 | byte、short、int、long、float、double、char、boolean、class、interface、enum、void |
定义流程控制的 | if、else、switch、case、default、while、do、for、break、continue、return |
访问权限修饰符 | private、protected、public |
定义类、函数、变量修饰符 | abstract、final、static、synchronized |
定义类与类之间的关系 | extends、implements |
定义建立实例及引用实例、判断实例 | new、this、super、instanceof |
包 | package、import |
其他修饰符关键字 | native、strictfp、transient、volatile、assert |
定义数据类型值得字面值 | true、false、null |
保留字
现有Java版本尚未使用,但以后得版本可能会作为关键字使用。如goto、const
标识符(identifier)
Java对各种变量、方法和类等要素命名时使用的字符序列称为标识符
凡是可以字命名得都是标识符
标识符命名规则:
① 字母+0-9+_或$
② 数字不可以开头
③ 不可以使用关键字和保留字,但允许包含关键字和保留字 如:intTest
④ Java严格区分大小写、长度无限制
⑤ 标识符不能包含空格
详细规范:——《代码整洁之道》
*1、项目名全部小写
*2、包名全部小写.
*3、类名首字母大写,其余组成词首字母依次大写.
*4、变量名,方法名首字母小写,如果名称由多个单词组成,除首字母外的每个单词的首字母都要大写.
*5、常量名全部大写
关键字static
- static修饰的变量和方法从属于类
- 普通变量的变量和方法从属于实例化的对象
- 静态方法不能调用非静态成员,否则会编译报错
- static的作用,主要是为了方便在没有创建对象的时候使用方法或变量。
- static修饰的方法和变量的加载加载会随最开始类加载时候一起加载
- 静态方法中不可以出现
this
关键字
static 修饰成员变量
- 静态变量被所有对象共享,在内存中只有一个副本,在类初次加载初始化时才会初始化
- static成员变量初始化顺序按照定义顺序进行初始化
static 修饰成员方法
静态方法会随着类的加载优先加载到方法区中,在main方法中调用静态方法中时,先在方法区找到方法将静态方法提到栈中执行
实例方法会在实例化对象加载到对内存后,在在方法区加载实例方法,再进行调用。
static 工具类
- 类中都是静态方法,每个方法都是以完成一个共用的功能为目的,这个类是用来给系统开发人员共同使用的
- 工具类的构造器必须私有化
创建工具类只是为了使用方法,由此工具类中的方法不使用实例方法,如果新建对象只是为了调用方法,会造成不必要的内存浪费。
static 代码块
静态代码块
static{
.......
}
- 与类一起优先加载,在mian方法之前加载,自动触发,只执行一次
- 可用于初始化静态资源
static应用——单例设计模式
- 单例模式:保证系统中应用该模式的类,从始至终只有一个实例
如,我们平常的电脑中只需要一个任务管理器对象,这样可以节省内存
饿汉式——类加载是就创建好实例(利用static的特性)
- 定义类,私有化构造器
- 定义一个静态变量存储一个实例化对象。
public class Hungery {
//定义一个实例化对象
public static Hungery hungery = new Hungery();
public int data1;
//私有换构造器
private Hungery(){};
//方法
....
}
懒汉式
- 构造器私有化
- 定义静态变量存储对象
public class Lazy {
//私有化构造器
private Lazy(){};
//定义静态变量
private static Lazy lazy;
//提供方法返回一个单例对象
public Lazy getInstance(){
if (lazy == null){//当前无实例,新建实例
return new Lazy();
}else {
return lazy;
}
}
}
二、变量
- 变量必须先声明再使用
- 变量都定义在其作用域内,变量只能在作用域内使用
- 在使用变量前要先初始化
- 同一个作用域内不能定义同名的变量
基本数据类型
8种基本数据类型
整型:byte、short、int、long
浮点型:float、double
布尔型:boolean
八种基本数据类型的定义
//各种数据类型定义
char a1 = '\u0043';
byte a = 'A';
int b = 10;
long c = 156468796476L;//末尾加上L
short d = 12543;
float e = 10;
double f = 12.3F;
boolean g = true;
System.out.println(a1+"\t"+a+"\t"+b+"\t"+c+"\t"+d+"\t"+e+"\t"+f+"\t"+g);
输出:
C 65 10 156468796476 12543 10.0 12.300000190734863 true
基本数据类型变量间转换
- 自动类型提升
//低类型向高类型转换
double i = 0.3//float 转 double
double j = 1 //int 转 double
byte a1 = 2;
int b1 = 12;
int c1 = a1 + b1;//两种不同类型发生运算时,发生自动类型提升
结论:当容量小的数据类型的变量与容量大的数据类型的变量做运算时,结果自动提升为容量大的数据类型
byte、char、short---->int----->long---->float--->double
特别的:当byte、char、short三种类型的变量做运算时,结果为int型,因为这三种类型在参加运算时,为了避免结果太大,直接将其转换为int类型后再参与计算,计算结果自然也是intt
- 强制类型转换(高类型转低类型)
//高类型转低类型
int i = (int) 4.5;//float 转 int
强制类型转换可能会造成精度损失或内存溢出,通常String不能直接转换成基本类型,但通过基本类型的包装类(Integerparse.Integer(String s))可以实现
小数强制转换为整数会直接截断整数
基本数据类型与String间转换
String类型的变量只能和基本数据类型做连接运算,运算的结果仍为String
int i = 100;
String str = i+" ";
String j = "分数为:"+100;//分数为:100
进制间转换
对于整数,有四种表示方式
>二进制:0,1,满二进一,以 0b 或 0B 开头
>十进制 :0-9 满十进一
>八进制:0-7 满八进一,以0开头表示
>十六进制:0-9及(A-F),满16进一,以 0x 或 0X 开头A-F不区分大小写
public static void main(String[] args) {
int num1 = 0b110;
int num2 = 110;
int num3 = 0127;
int num4 = 0x110A;
System.out.println(num1);
System.out.println(num2);
System.out.println(num3);
System.out.println(num4);
//method();
}
输出
6
110
87
4362
- 负数的原码
将一个数值转换为二进制数,最高位为符号位 - 负数的反码
对原码按位取反,只是最高位(符号位)确定为1 - 负数的补码
反码加1
** 正数的原码、反码、补码都相同
三、运算符
|与||,&与&&: 文章链接.
三元运算符:
(条件表达式)?语句1:语句2;
//条件表达式为true执行语句1,为false执行语句2
//比较三个数的大小
int a = 1;
int b = 2;
int c = 5;
int max = (((a > b)?a:b)>c)?((a > b)?a:b):c;
System.out.println(max);
输出:5
凡是用三元运算符的地方都可以代替if-else控制语句,但不建议使用三元运算符,因为if-else语句会让程序逻辑更加可读
自增与自减注意
++、--
的位置,再变量前,使用计算后的结果参与计算,在变量后,先用当前值参与计算,在执行自增自减运算。
四、流程控制
分支结构if
//if - else举例
public static void main(String[] args) {
//从键盘获取输入,若为负数,直接return,若为正数比较两数大小
Scanner scan = new Scanner(System.in);
int a = scan.nextInt();
int b = scan.nextInt();
if(a < 0 || b < 0) {
System.out.println("请输入两个正数!");
}else {
if (a > b) {
System.out.println(a);
}else System.out.println(b);
}
//method();
}
输入1
5
9
输出1
9
输入2
-71
1
输出2
请输入两个正数!
注意:if-else中的else总是去匹配最近的那个if,书写时要稍微注意。
分支结构switch case
switch的表达式类型只能是byte,short,int,char,JDK5开始支持枚举,jdk7开始支持string,不支持double,float,long,因为这些类型不精确。case中的值一定是确定的不重复的字面值。不能用变量名代替
//switch-case举例
public static void main(String[] args) {
//输入一个整数,x = infonumber / 10;y = infonumber % 10;
//对x和y分别通过switch语句做判断输出对应的句子
int infonumber, x, y;
String sentence1, sentence2;
sentence1 = null;
sentence2 = null;
Scanner in = new Scanner(System.in);
infonumber = in.nextInt();
x = infonumber / 10;
y = infonumber % 10;
switch (x) {
case 1: {
sentence1 = "Unreadable";
break;
}
case 2: {
sentence1 = "Barely readable, occasional words distinguishable";
break;
}
case 3: {
sentence1 = "Readable with considerable difficulty";
break;
}
case 4: {
sentence1 = "Readable with practically no difficulty";
break;
}
case 5: {
sentence1 = "Perfectly readable";
break;
}
}
switch (y) {
case 1: {
sentence2 = "Faint signals, barely perceptible";
break;
}
case 2: {
sentence2 = "Very weak signals";
break;
}
case 3: {
sentence2 = "Weak signals";
break;
}
case 4: {
sentence2 = "Fair signals";
break;
}
case 5: {
sentence2 = "Fairly good signals";
break;
}
case 6: {
sentence2 = "Good signals";
break;
}
case 7: {
sentence2 = "Moderately strong signals";
break;
}
case 8: {
sentence2 = "Strong signals";
break;
}
case 9: {
sentence2 = "Extremely strong signals";
break;
}
}
System.out.println(sentence2 + "," + sentence1);
// method();
}
输入
4
输出
Fair signals,null
注意:switch-case结构一旦匹配到符合的语句才回进入case中的执行语句,且如果case中没有break关键字;程序会继续执行之后的所有case中的语句,直到遇到break(switch的穿透问题);或程序结束。
循环结构for
for循环举例一:九九乘法表
public static void main(String[] args) {
// 输入一个整数,x = infonumber / 10;y = infonumber % 10;
// 对x和y分别通过switch语句做判断输出对应的句子
for (int i = 1; i <= 9; i++) {
for (int j = 1; j <= i; j++) {
System.out.print(i + "*" + j + "=" + (i * j) + " ");
}
System.out.println();
}
// method();
}
输出:
1*1=1
2*1=2 2*2=4
3*1=3 3*2=6 3*3=9
4*1=4 4*2=8 4*3=12 4*4=16
5*1=5 5*2=10 5*3=15 5*4=20 5*5=25
6*1=6 6*2=12 6*3=18 6*4=24 6*5=30 6*6=36
7*1=7 7*2=14 7*3=21 7*4=28 7*5=35 7*6=42 7*7=49
8*1=8 8*2=16 8*3=24 8*4=32 8*5=40 8*6=48 8*7=56 8*8=64
9*1=9 9*2=18 9*3=27 9*4=36 9*5=45 9*6=54 9*7=63 9*8=72 9*9=81
for循环举例二:100以内的所有素数
public static void main(String[] args) {
lable: for (int i = 2; i <= 100; i++) {
for (int j = 2; j <= Math.sqrt(i); j++) {
if (i % j == 0) {
continue lable;
}
}
System.out.println(i);
}
// method();
}
for循环举例二:数组的冒泡排序
public static void main(String[] args) {
int temp = 0;
int[] arr = new int[] {12,64,54,465,-1,-15,9,199,456,0,8,4,798};
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]) {
temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
for(int x : arr) {
System.out.print(x + "\t");
}
}
增强for循环
int [] numbers = new int[20];
// for(elemtypeName elemName : 遍历的对象名)
//遍历输出数组中的元素
for(int a:numbers){
System.out.println(a);
}
while 举例100以内的偶数
//要使用do - while 结构,必须在进入结构前设置初始条件
int i = 1;
//如果初始条件都不满足判断,那么while内的循环控制体就一次也不会执行
while (i <= 100) {
if (i % 2 == 0) {
System.out.println(i);
}
i++;
}
do-while举例
特点:无论条件是否满足,循环体内的代码都会先执行一次
// do-while 100以内的偶数及个数
//要使用do - while 结构,也必须在进入结构前设置初始条件
int i = 1;
int count = 0;
do {
//无论i是否满足条件,do 里的语句都会执行至少一遍
if (i % 2 == 0) {
System.out.println(i);
count++;
}
i++;
} while (i <= 100);
System.out.println(" " + count);
初始条件 i = 200 时输出
200
1
循环次数可知时用for循环,不可知时用while循环
关键字:break、continue
break主要用于结束循环
public static void main(String[] args) {
//遍历一个数组,找到某个数字的第一个位置就跳出循环
int arry[] = new int[] {1,5,9,5,6,7,4,5,9,5};
for(int i = 0;i < arry.length;i++) {
if(arry[i] == 9){
System.out.println("9是第"+(i+1)+"个元素");
break;
}
System.out.println("————————");
}
}
输出
————————
————————
9是第3个元素
continue:结束本次循环,开始下次循环
public static void main(String[] args) {
//遍历一个数组,找到某个数字的第一个位置就跳出循环
int arry[] = new int[] {1,5,9,5,6,7,4,5,9,5};
int count = 0;
int j = 0;
for(int i = 0;i < arry.length;i++) {
if(arry[i] == 9){
count++;
System.out.println("第"+count+"个9是第"+(i+1)+"个元素");
continue;//continue之后结束本次循环,continue之后的语句不再执行
}
System.out.println(++j);//此语句只会执行8次
}
}
输出:
1
2
第1个9是第3个元素
3
4
5
6
7
第2个9是第9个元素
8
对指定循环体进行操作
关键字 break 、continue与 if - else 结构中的else有相同之处,else遵守就近原则匹配最近的 if ,break 和 continue 也一样,没有特殊说明的情况下,只会break 或 continue当前循环,但有时我们需要跳出指定循环,我们可以采用给循环体命名的手段来解决这个问题。
** 命名方式 **
/* 循环体名: for (;;) {
// 第一层循环体
for (;;) {
// 第二层循环体
continue 循环体名;// 直接跳出内层循环,继续执行外层循环
for (;;) {
// 第三层循环体
break 循环体名;//直接跳出最外层循环
}
}
}
*/
LOOP: for (int i = 0; i <= 4; i++) {
for (int j = 1; j <= 10; j++) {
if (j % 4 == 0) {
break LOOP;
} else {
System.out.println("*");
continue LOOP;
}
}
System.out.print("------");//此语句不会执行
}
输出
*
*
*
*
*
random随机数
生成随机数的四种方法
包 java.util.Random
Random r = new Random(10);//(0-9)
int data = r.nextInt
五、修饰符
权限修饰符
private->缺省->protected->public(范围从小到大)
private
同一个类中可以访问
缺省
同一个包中可以访问
protected
不同包下的子类可以访问
public
不同包下的无关类也可以访问
final修饰符
- 修饰类,类为最终类,不可以被继承
- 修饰方法,方法为最终方法,不能被重写
- 修饰变量,变量第一次赋值后就不能在被赋值。
- 修饰基本类型,则基本类型的值不可改变
- 修饰引用类型,则修饰的地址值不可改变,但对象中的具体内容可以改变
public static final修饰常量
六、常量——public static final
常量执行原理
- 编译阶段进行
宏替换
,把使用常量的地方全部替换成真是的字面值 - 这样做可以让使用常量的程序执行性能与使用字面值的程序性能一致。
命名规范
所有字母大写用"_"分隔不同单次
七、 枚举
- java中的一种特殊类型
- 为了做信息的标志和信息的分类
- 枚举类都是最终类妙不可言被继承
- 枚举类的构造器都是私有的,枚举类对外不能创建对象
- 枚举是一种多例模式
- 定义枚举格式
修饰符 enum 枚举名称{
}
enum Season{
SPRING,SUMMER,AUTUMN,WINTER
}
反编译上述代码结果
Complied from "Season.java"
public final class Season extends java.lang.Enum<Season>{
public static final Season SPRING = new Season();
public static final Season SUMMER= new Season();
public static final Season AUTUMN = new Season();
public static final Season WINTER = new Season();
public static Season[] values();
public static Season ValueOf(java.lang.String);
}
八、abstract
抽象类
- 有抽象方法的类一定是抽象类
- 一般用作父类-每个子类对于同一个方法的实现方式不一样,此时就将该方法声明成抽象方法,具体的事项交给子类去具体实现
- 一般类有的构造器等,抽象类也都具有
模板方法模式
系统中出现同一个功能多出在开发,而该功能中的大部分代码是一样的,只有其中部分可能不同
- 将功能定义成所谓的模板方法放在抽象类中,模板方法中只定义通用且能确定的代码
- 模板方法中不能决定的功能定义成抽象方法让具体子类去实现
- 模板方法建议使用final修饰:模板方法是给子类直接使用的,不是给子类重写的,final是更专业的写法
抽象方法
- 没有具体的方法体
- 得到抽象方法,意味着失去实例化对象的能力
具有抽象方法的类一定是抽象类,但抽象类中不一定有抽象方法。
一个类继承了抽象类必须重写抽象类的全部方法,否则这个类也必须定义为抽象类
九、接口 interface、implements
jdk8之前,接口只能包含抽象方法和常量。
public interface InterfaceDemo{
//常量
public static final DATA_A = 1;
public static final DATA_B= 1;
...
抽象方法
public abstract method1();
public abstract method2();
...
}
接口的多继承
一个接口可以继承多个接口,一个接口也可以被多个类实现(implements)
接口中的方法体
在实际开发中,又是会对接口进行扩展,但这样会导致牵一发而动全身,修改所有的实现类,所以1.8以后允许接口直接定义带有方法体的方法。
默认方法JDK8
- 由接口的实现类对象来调用
default void method(){
//具体实现细节
...
}
静态方法
接口的静态方法必须用本身的接口名来调用
默认使用public修饰
static void method(){
...
}
私有方法jdk9
- 私有的实例方法 必须使用private修饰,从JDK9才开始的
- 只能在本类中被其他的默认方法或者私有方法访问
ptivate void go(){
//具体实现....
...
}
十、多态
- 多态指,同类型对象,执行同一个行为,会表现出不同行为的特征
- 多态可实现组件式的解耦,便于代码功能的扩展维护
- 多态下不能调用子类的独有行为,但可以通过类型强转方式解决此弊端。
instanceof 判断当前类型的真实类型
为了避免类型强转以后的类型与实际类型不符,所以在在类型转换之前,使用instanceof
判断当前类型的真实类型以后,在进行强制转换
if (data instance of A){
A a = (A) data;
...
}else if(data instanceof B){
B b = (B) data;
...
}
十一、内部类
- 定义在类内部的类
public class People{
...
public class Heart{
}
}
- 内部类可以方便访问外部类成员,包括私有成员
- 内部类提供了更好的封装性,内部类本身就可以用private protected 等修饰,封装性还可以做更多控制
静态内部类
- static 修饰,属于外部类本身,可以直接访问外部类的静态成员
- 静态内部类不可以直接访问外部类的实例对象,可以通过外部类的实例对象间接访问
public class People{
...
public static class Heart{
}
}
创建静态内部类对象
格式:外部类名.内部类名 对象名 =new
外部类名.内部构造器;
成员内部类
无tatic修饰,属于外部类的对象
Jdk16之前成员内部类中不可定义静态成员
public class Outer{
...
public static class Inner{
private String str;
private int age;
public static int a//jdk16以前会报错
}
}
创建成员内部类对象
格式:Outer.Inner in=new
Outer.new Inner();
匿名内部类(重点)—方便创建子类对象
本质为没有名字的局部内部类,主要目的式方便创建子类对象,简化代码编写。
new 类|抽象类名|接口名(){
重写方法;
}
Employee a = new Employee(){
public void work(){...}
}