# java基础语法

java文件

一个.java文件可以有多个类,但只能有一个public的类,并且public的类名必须与文件名相一致。若还有多个类,非public修饰的类可以与文件名不同。并且有几个类就会被编译为几个.class文件。

每个源文件只能有一个public类

简单的说,它就是我们一个约定俗成的规范。

在java编程思想(第四版)一书中的描述:

  • 每个编译单元(文件)都只能有一个public类,这表示,每个编译单元都有单一的公共接口,用public类来表现。该接口可以按要求包含众多的支持包访问权限的类。如果在某个编译单元内有一个以上的public类,编译器就会给出错误信息。

  • public类的名称必须完全与含有该编译单元的文件名相同,包含大小写。如果不匹配,同样将得到编译错误。

文件内的每个类都会生成单独的class文件

一个编译单元(java文件)可以存在多个类,在编译时产生多个不同的.class文件,.class文件便是程序运行的数据来源。

总结:

咱们的一个.java文件被编译,是有一定的原则的,机器默认办事,肯定存在“潜规则”,这个每个java文件唯一被public修饰且与文件名相同的类名就是一个编译的入口,从他开始完成对整个文件中所有类的编译。从而生成对应的多个.class文件

// HelloWorld.java
public class HelloWorld{
    public static void main(String args[]){
        System.out.println("Hello World");
    }
}
class Test{
    public static void main(String args[]){
        System.out.println("Test");
    }
}
//javac HelloWorld.java
// java HelloWorld---> HelloWorld
// java Test ---> Test

规范

  1. Java对大小写敏感,如果出现了大小写拼写错误,程序无法运行。

  2. 关键字public被称作访问修饰符(access modifier),用于控制程序的其它部分对这段代码的访问级别。

  3. 关键字class 的意思是类。Java是面向对象的语言,所有代码必须位于类里面。

  4. 一个源文件中至多只能声明一个public的类,其它类的个数不限,如果源文件中包含一个public 类,源文件名必须和其中定义的public的类名相同,且以“.java”为扩展名。

  5. 一个源文件可以包含多个类class。

  6. 正确编译后的源文件,会得到相应的字节码文件,编译器为每个类生成独立的字节码文件,且将字节码文件自动命名为类的名字且以“.class”为扩展名。

  7. main方法是Java应用程序的入口方法,它有固定的书写格式:

  8. public static void main(String[] args) {…}

  9. 在Java中,用花括号划分程序的各个部分,任何方法的代码都必须以“{”开始,以“}”结束, 由于编译器忽略空格,所以花括号风格不受限制。

  10. Java中每个语句必须以分号结束,回车不是语句的结束标志,所以一个语句可以跨多行。

变量和常量命名规范

(规范是程序员的基本准则,不规范会直接损害你的个人形象):

  • 所有变量、方法、类名:见名知意

  • 类成员变量:首字母小写和驼峰原则: monthSalary

  • 局部变量:首字母小写和驼峰原则

  • 常量:大写字母和下划线:MAX_VALUE

  • 类名:首字母大写和驼峰原则: Man, GoodMan

  • 方法名:首字母小写和驼峰原则: run(), runRun()

关键字 标识符

关键字就不列出来了,百度一下都能查到,只说明一点,java里关键字都是小写的,一般编辑器里都是蓝色的

标识符由26个大小写英文字母、0-9数字、_$符号组成

  • 数字不可以开头
  • 不可以使用关键字
  • 严格区分大小写

Java不采用通常语言使用的ASCII字符集,而是采用Unicode这样标准的国际字符集。因此,这里字母的含义不仅仅是英文,还包括汉字等等。但是不建议大家使用汉字来定义标识符

注释

单行注释: // 多行注释: /* */ java特有文档注释(能被javadoc提取): /** * * */ 注释不进入字节码 注释可用于排除程序错误点

进制

1byte = 8bit 二进制:0 1,满2进1 八进制:0-7,满8进1,开头0 十进制:0-9,满10进1 十六进制:0-9,A-F,满16进1,开头0x

byte字节=8个二进制位bit 1k = 1024byte 1M = 1024k 现在硬盘厂商一般都以1000计数

进制转换(参考毕向东进制课程)

  • 二进制转十进制就是每一位代表的数字相加,反之除以2得出二进制
  • 二进制转八进制是每3个一组算出10进制,前面加0,反之亦然
  • 二进制转十六进制是每4个一组算出10进制,前面加0x,反之亦然
  • 1111-1111为255,IP是4个字节组成,每个字节8位,所以最大255
  • 整数都是32位表示,负数就是取反加1

基本数据类型

java数值和javascript不同,javascript数值都属于Number,只有整数和浮点数之分,在内存里其实都是浮点数。java的数值分为byte,short,int,long,float,double

变量

由名字,类型,数据确定的内存空间,数据不确定时就定义变量,记录不确定数据

变量本质上就是代表一个”可操作的存储空间”,空间位置是确定的,但是里面放置什么值不确定。我们可通过变量名来访问“对应的存储空间”,从而操纵这个“存储空间”存储的值。

Java是一种强类型语言,每个变量都必须声明其数据类型。变量的数据类型决定了变量占据存储空间的大小。 比如,int a=3; 表示a变量的空间大小为4个字节

赋值和初始化

变量的初始化指的是,在变量被定义之后,系统需要为变量分配内存空间,但是存储的值是不确定的,所以需要对这个空间进行初始化,以确保程序的安全性和确定性;而变量的赋值指的是变量在分配空间之后的某个时间里对变量的值进行刷新操作(修改存储空间内的值)。

初始化,即开辟内存区域时赋的值。不指定的话开辟时赋该类型的默认值。举个例子,声明:告诉java编译器要声明一个变量(告诉小区保安,我一会儿要在这儿停车)初始化:“占了个车位”(那我给你占个车位吧,你看是用我们的车模型(默认值)还是您的车(自己指定的值)来占位呢)注:只要空着的车位就可能被占。详情如下:

局部变量需要初始化

  • 在java中,类的成员变量不用初始化即可直接使用,JVM会自动初始化,原始变量如int char short long byte初始化为0,float double初始化为0.0,boolean初始化为false,对象 初始化为null(也就是默认给你放个车模型就去)

  • 方法中,java可在使用时再声明,但必须赋值后才能使用。 那么为什么方法内声明的变量jvm不进行自动初始化呢?原因如下: ①编程语言不止一家,考虑到代码的移植性和兼容性。同时这也是编程的一种好习惯。 ②这个和java的垃圾回收机制也有关系,以引用型变量为例,只有显式的给变量初始化了(赋初值为null),那么上次调用的变量就可以更快的被回收,否则会影响内存

  • 首先纠正一个观点,全局变量和局部变量都是需要初始化的。这是因为使用没有初始化的变量是不安全的。只不过全局变量的初始化工作可以交给JVM。而为什么局部变量不可以呢。我认为是出于性能等多方面的考虑。

  • 成员变量的值存放于堆中,JVM在分配内存时将整块区域置为零即 完成了初始化,方便快捷。而局部变量运行时被分配于栈中,量大,生命周期短,如果由JVM完成初始化,将是一笔很大的性能开销。所以java明文规定局部变量必须初始化

int a;
public void init() {
    int b;
    System.out.println(a);
    System.out.println(b); //The local variable b may not have been initialized
}

数据类型

浮点数的表示

浮点数精度

浮点数运算都是不精确的

java.math包下面的两个有用的类:BigInteger和BigDecimal,这两个类可以处理任意长度的数值。BigInteger实现了任意精度的整数运算。BigDecimal实现了任意精度的浮点运算

BigDecimal bd = BigDecimal.valueOf(1.0);
bd = bd.subtract(BigDecimal.valueOf(0.1));
bd = bd.subtract(BigDecimal.valueOf(0.1));
bd = bd.subtract(BigDecimal.valueOf(0.1));
bd = bd.subtract(BigDecimal.valueOf(0.1));
bd = bd.subtract(BigDecimal.valueOf(0.1));
System.out.println(bd);//0.5
System.out.println(1.0 - 0.1 - 0.1 - 0.1 - 0.1 - 0.1);//0.5000000000000001

char

java中char占两个字节,可以存放一个中文字符 char c = '陈'; 是可以的 但 char c = 'AB' 就不对了,AB虽然也占用两个字节,但是两个字符,不是一个字符。 char类型只能存放一个字符,一个字符可以占两个字节。

数据类型转换

编码表 UNICAODE ASCII

二进制和生活中文字的对应关系表 01100001 -- a 01100010 -- b

不同类型的数据不能直接运算,除了char

int a = 'a' + 1;
System.out.println(a);
//98
System.out.println('a'+'b'); //195

自动类型转换从低级到高级 byte,short,char—> int —> long—> float —> double

自动类型转换

自动类型提升 自动类型转换是指:数字表示范围小的数据类型可以自动转换成范围大的数据类型

实线表示自动转换时不会造成数据丢失,虚线则可能会出现数据丢失问题

byte bNum = 12;
int iNum = bNum;
System.out.println(iNum); // 12

char cChar = '1';
int cNum = cChar;
System.out.println(cNum); // 49  字符转化为数字,变为ascii码

int count = 100000000;
int price = 1999;
long totalPrice = count * price;
System.out.println(totalPrice);  // -1963462912
// int*int超出了int最大值,将count类型改为long,乘法类型会自动提升

int year = 20;
int money = 1000000000;
long total = year * money;
System.out.println(total); //-1474836480
long total1 = year * (long)money;
System.out.println(total1); //20000000000


//可以将整型常量直接赋值给byte、 short、 char等类型变量,而不需要进行强制类型转换,只要不超出其表数范围即可
int a = 12;
short s = 12;
s=a; //Type mismatch: cannot convert from int to short
System.out.println(s);

long l = 23.1f; //cannot convert from float to long
float f = 123123123l;

强制转换

一般发生在数字表示范围大的数据类型转换成范围小的数据类型

byte b = 3;
b = (byte)(b + 4);

int iNum = 127;
byte bNum = (byte)iNum;
System.out.println(bNum); //127

int nNum = 128;
byte yNum = (byte)nNum;
System.out.println(yNum); // -128 超出byte最大值溢出

float fNum = 12.8f;
int tNum = (int)fNum;
System.out.println(tNum);  // 12 抛弃小数位

浮点数之间转换

float fNum = 12.3f;
double dNum = fNum;
System.out.println(dNum); //12.300000190734863

double oNum = 12.3;
float lNum = (float)oNum;
System.out.println(dNum); //12.300000190734863

字符串转换

字符串转基本类型

String s = "123";

System.out.println(Byte.parseByte(s));
System.out.println(Short.parseShort(s));
System.out.println(Integer.parseInt(s));
System.out.println(Long.parseLong(s));
System.out.println(Double.parseDouble(s));
System.out.println(Float.parseFloat(s));
System.out.println(Boolean.parseBoolean(s));  // false
System.out.println(Boolean.parseBoolean("true")); // true
System.out.println(Boolean.parseBoolean("false")); // false
System.out.println(Boolean.parseBoolean("blue")); // false
System.out.println(Boolean.parseBoolean("null")); // false
System.out.println(s.charAt(0));

基本类型转字符串

byte b = 100;
short s = 100;
int i = 100;
long l = 100;
float f = 100.11f;
double d = 100.11;
boolean bl = true;
boolean rbl = false;

System.out.println(b+"");
System.out.println(s+"");
System.out.println(i+"");
System.out.println(l+"");
System.out.println(f+"");
System.out.println(d+"");
System.out.println(bl+"");  // true
System.out.println(rbl+""); // false

代码块

局部代码块可以定义局部变量的声明周期

{
    int m=89; //外部无法访问
    System.out.println("{}: "+m);
}

控制语句

循环

int i=0;
while (i<10){
	System.out.println(i);
	i++;
}
// 0 1 2 3 4 5 6 7 8 9

int x =1;
while(x<3){
    System.out.println("x="+x);
    // System.out.println(x++); //1,2
    x++;
    System.out.println(x); //2,3
}

//打印能被5整除的数,每五个换行
int j = 0;
for (int i = 1; i <= 1000; i++) {
    if (i % 5 == 0) {
        System.out.print(i);
        j++;
    }
    if (j % 5 == 0) {
        System.out.println();
        j = 0;
    }
}

int n=0;
do{
	System.out.println(n);
	n++;
}while (n>0 && n<10);
// 0 1 2 3 4 5 6 7 8 9  第一次始终都会执行

for(int m=0;m<10;m++){
	System.out.println(m);
}
// 0 1 2 3 4 5 6 7 8 9

// 初始表达式执行一次,循环条件,循环后表达式
int xx = 1;
for(System.out.println('a');xx<3;System.out.println('c')){
    System.out.println('b');
    xx++;
}

int [] numbers = {10, 20, 30, 40, 50};

for(int x : numbers ){
	System.out.print( x );
	System.out.print(",");
}
// 10,20,30,40,50,  用于数组的增强for循环

无限循环

while(true)
for(;;)

嵌套循环 正直角三角:y<=x 倒直角三角:y=x

/*
*****
****
***
**
*
*/

for(int n=1;n<=5;n++){
    for(int m=n;m<=5;m++){
        System.out.print('*');
    }
    System.out.println();
}

/*
*
**
***
****
*****
*/

{
    for(int x=1;x<=5;x++){
        for(int y=1;y<=x;y++){
            System.out.print('*');
        }
        System.out.println();
    }
}

/*
1*1=1
1*2=2 2*2=4
1*3=3 2*3=6 3*3=9
*/

for(int x=1;x<=9;x++){
    for(int y=1;y<=x;y++){
        System.out.print(y+"*"+x+"="+x*y + "\t");
    }
    System.out.println();
}

/*
* * * * *
 * * * *
  * * *
   * *
    *
*/

{
    for(int x=1;x<=5;x++){
        for(int y=1;y<x;y++){
            System.out.print(" ");
        }
        for(int z=x;z<=5;z++){
            System.out.print("* ");
        }
        System.out.println();
    }
}

break continue label

for(int i=0;i<3;i++){
	for(int j=0;j<3;j++){
		if(i==1 && j==0)
			break;  // i=1的时候跳出内层循环
		System.out.println(i+""+j);
	}
}
// 00 01 02 20 21 22

for(int i=0;i<3;i++){
	for(int j=0;j<3;j++){
		if(i==1 && j==0)
			continue;  // i=1 j=0的时候跳出本次循环,继续内层循环
		System.out.println(i+""+j);
	}
}
// 00 01 02 11 12 20 21 22

test_lbl:
for(int i=0;i<3;i++){
	for(int j=0;j<3;j++){
		if(i==1 && j==0)
			continue test_lbl;  // 直接跳到'testlbl'所在循环
		System.out.println(i+""+j);
	}
}
// 00 01 02  20 21 22

//打印质数
outer:
for(int i=101;i<150;i++) {
    for(int j=2;j<i/2;j++) {
        if(i%j==0) {
            continue outer;
        }
    }
    System.out.println(i);
}

条件

int a=0;  

if(a==0){
	System.out.println("0"); // 如果为真就不往下走了
}else if(a<1){
	System.out.println("1");
}else{
	System.out.println("false");
}

相比三元运算,if可以执行也可以赋值

选择

//随机数
double r = Math.random();
int i = (int) (6 * r) + 1;
System.out.println(i);
if (i <= 3) {
    System.out.println("小");
} else {
    System.out.println("大");
}
    
//Random不好取范围
for (int x = 0; x < 10; x++) {
    Random random = new Random();
    Thread.sleep(100);
    System.out.print(random.nextInt(100) + " ");
}


switch (a){
	case 0:
		System.out.println("0");
		break;
	case 1:
		System.out.println("1");
		break;
	default:
		System.out.println("false");
}

char c = 'a';
int r = (int)(26*Math.random());
System.out.println(r);
int ss = c+r;
System.out.println(ss);
char c2 = (char)(c+r);
System.out.println(c2);
switch (c2) {
case 'a':
case 'e':
case 'i':
case 'o':
case 'u':
    System.out.println("元音");
    break;
default:
    System.out.println("辅音");
    break;
}

//不同条件同执行
int month = 3;
switch(month){
    case 3:
    case 4:
    case 5:
        System.out.println("春季"); 
        break; 
}

运算符

算术运算符

  • 加法 + - 相加运算符两侧的值
  • 减法 - - 左操作数减去右操作数
  • 乘法 × - 相乘操作符两侧的值
  • 除法 / - 左操作数除以右操作数
  • 取余 % - 左操作数除以右操作数的余数
  • 自增 ++ 操作数的值增加1
  • 自减 -- 操作数的值减少1
  • 连接符 + 加上字符串都为字符串
  1. 如果两个操作数有一个为Long, 则结果也为long。
  2. 没有long时,结果为int。即使操作数全为short,byte,结果也是int。
  3. 如果两个操作数有一个为double,则结果为double。
  4. 只有两个操作数都是float,则结果才为float。
  5. “余数”符号和左边操作数相同,如:7%3=1,-7%3=-1,7%-3=1
int a1 = 6370;
a1 = a1 / 1000;
System.out.println(a1);
// 小心除法

boolean aa = true;
String bb = aa + "1";
System.out.println(bb);
//true1


int a = 10,b=20,c=30,d=40;

System.out.println(a+b); //30
System.out.println(a-b); //-10
System.out.println(a*b); //200
System.out.println(b/a); //2
System.out.println(b%a); //0
System.out.println(c++); //30 输出旧的值再自增
System.out.println(++c); //32 输出自增后的值,上一行已经自增到31
System.out.println(d--); //40 同理
System.out.println(--d); //38 同理

int a=3;
a *= b+3;  //a=a*(b+3)
System.out.println(a); //15

关系运算符

  • == 相等
  • != 不相等
  • 大于

  • < 小于
  • = 大于等于

  • <= 小于等于

一般都用于数字,但char也可以用于比较,会自动转换为数值

int a = 10,b=20;

System.out.println(a==b); //false
System.out.println(a!=b); //true
System.out.println(a>b); //false
System.out.println(a<b); //true
System.out.println(a>=b); //false
System.out.println(a<=b); //true

char c1='a';
char c2='b';
System.out.println(c1>c2); //false

位运算符

| 的特点都是将1的位保留 & 的特点是&1的位都保留

逻辑运算符

连接两个boolean

boolean a = true,b=false;

// 短路运算,只要左边可以决定值了,右边不会运算
System.out.println(a && b); //false
System.out.println(a || b); //true
System.out.println(!a); //false
System.out.println(!b); //true

System.out.println(true ^ true); // false
System.out.println(true ^ false);// true
System.out.println(false ^ true);// true
System.out.println(false ^ false); // false

//1>2的结果为false,那么整个表达式的结果即为false,将不再计算2>(3/0)
boolean c = 1>2 && 2>(3/0); //false
System.out.println(c);
//1>2的结果为false,那么整个表达式的结果即为false,还要计算2>(3/0),0不能做除数,//会输出异常信息
boolean d = 1>2 & 2>(3/0); //Exception in thread "main" java.lang.ArithmeticException: / by zero
System.out.println(d);

赋值运算符

= 简单的赋值运算符,将右操作数的值赋给左侧操作数 += 加和赋值操作符,它把左操作数和右操作数相加赋值给左操作数 -= 减和赋值操作符,它把左操作数和右操作数相减赋值给左操作数 *= 乘和赋值操作符,它把左操作数和右操作数相乘赋值给左操作数 /= 除和赋值操作符,它把左操作数和右操作数相除赋值给左操作数 %= 取模和赋值操作符,它把左操作数和右操作数取模后赋值给左操作数 <<= 左移位赋值运算符

= 右移位赋值运算符 &= 按位与赋值运算符 ^= 按位异或赋值操作符 |= 按位或赋值操作符

条件运算符 三元运算符

int a , b;
a = 10;
// 如果 a 等于 1 成立,则设置 b 为 20,否则为 30
b = (a == 1) ? 20 : 30;
System.out.println( "Value of b 成员变量:成员变量是定义在类is : " +  b ); //30

优先级

函数

函数只负责一种功能,负责了计算就不要负责输出

重载 同一个类,同名,参数个数或类型不同,和返回值无关,只有返回值不同不构成方法的重载

递归

递归结构包括两个部分:

  1. 定义递归头。解答:什么时候不调用自身方法。如果没有头,将陷入死循环,也就是递归的结束条件。
  2. 递归体。解答:什么时候需要调用自身方法。
long d1 = System.currentTimeMillis();
System.out.printf("%d阶乘的结果:%s%n",20,factorial(20));
long d2 = System.currentTimeMillis();
System.out.printf("递归耗时:%s%n",d2-d1);

long d3 = System.currentTimeMillis();
System.out.printf("%d阶乘的结果:%s%n",20,loop(20));
long d4 = System.currentTimeMillis();
System.out.printf("循环耗时:%s%n",d4-d3);

//		20阶乘的结果:2432902008176640000
//		递归耗时:28
//		20阶乘的结果:2432902008176640000
//		循环耗时:1

static long factorial(int n) {
    if(n==1) {
        return 1;
    }else {
        return n * factorial(n-1);
    }
}

static long loop(long a) {
    long result = 1;
    while (a > 1) {
        result *= a * (a - 1);
        a -= 2;
    }
    return result;
}

简单的程序是递归的优点之一。但是递归调用会占用大量的系统堆栈,内存耗用多,在递归调用层次多时速度要比循环慢的多,所以在使用递归时要慎重

任何能用递归解决的问题也能使用迭代解决。当递归方法可以更加自然地反映问题,并且易于理解和调试,并且不强调效率问题时,可以采用递归;

在要求高性能的情况下尽量避免使用递归,递归调用既花时间又耗内存

内存

  1. 寄存器
  2. 本地方法区
  3. 方法区
  4. 栈 存储的都是局部变量 变量所属作用域一旦结束自动释放
  5. 堆 存储的是数组和对象,凡是new建立的都在堆中 特点:
    1. 每一个实体都有首地址
    2. 堆内存中每一个变量都有默认值
    3. 垃圾回收机制,java会定时回收堆中的垃圾(不再使用的实体)


书籍推荐