java基本概念

JVM,JRE与JDK

java语言有一个非常显著的特征就是跨平台性,无论在什么操作系统上,java程序都能够运行,这就得益于java的JVM。

  • JVM
     java虚拟机,简称JVM(java virtual machine),是运行java程序的假想计算机,是java程序的运行环境,我们编写的java代码,都运行在JVM上。
     JVM为什么能使java具有跨平台性呢?原因就在于每个操作系统都具有一个不同的JVM,这些JVM充当了java程序与操作系统之间的中介,java程序直接运行在JVM中,形成的字节码在转运到操作系统,使得java程序在不同平台不需要重新编译,具有跨平台性。
  • JRE与JDK
     JRE(Java Runtime Environment),是java程序运行时所需的环境,包括JVM和运行时所需要的核心类库,如果只需要运行java程序的话,那么只需要JRE即可。
     JDK(Java Development Kit),是java程序开发的工具包,包括JRE和开发人员使用的工具,如果需要开发java程序,那么就需要安装JDK了。
     这三者的关系是JDK>JRE>JVM,如下图所示

用cmd运行java程序

首先将cmd定位在java程序所在的文件夹中,然后依次使用javac命令和java命令,javac是对java程序进行编译成class字节码,java命令则是运行编译好的class文件,具体操作如下图所示,其中Test.java是我已经编写好的程序

java程序的注释

java程序的注释与c++类似,有2种注释方法:单行注释和多行注释
单行注释://注释内容,//之后的内容将被计算机忽略
多行注释:/* 注释内容,/* */之间的内容将被计算机忽略,可以跨行 */

java关键字和标识符的命名规范

 和c++语言类似,java也有预先定义的关键字,这些关键字都是全小写,都有特定含义,变量名和类名等都不能与关键字相同。java区分大小写,所以,命名时可以使用标识符的大写,但不建议。
 标识符就是我们自己定义的内容的名称,标识符可以包含英文字母,数字,美元符号$和下划线_,但标识符不能以数字开头
建议标识符的命名规范:

  • 类名规范:首字母大写,后面每个单词的首字母大写(大驼峰式)
  • 变量名规范:首字母小写,后面每个单词首字母大写(小驼峰式)
  • 方法名规范:同变量名

常量与变量

  • 1.常量分为字符串常量,整数常量,浮点数常量,字符常量,布尔常量,空常量,用const修饰
    字符串常量:String,以’\0’结束,如”abc”,”123”,占n+1个字节,其中n为组成字符串的字符个数。
    整数常量:short(2字节),int(4字节),long(8字节)
    浮点数常量:float(4字节),double(8字节)
    字符常量:char(1字节)
    布尔常量:bool(1字节)
    空常量:null(0字节)
  • 2.变量,即可以改变的常量,没有const修饰的常量,其类型与常量相同
    变量的使用注意事项:
  • 如果创建多个变量,变量名不可以重复
  • 对于float和long类型,字母后缀的F和L不要去掉
  • 如果使用byte和short类型的变量,右侧的数据值不要超过左侧类型的范围
  • 一定要对变量赋值后才能使用
  • 变量使用不能超过作用域

数据类型转换

  • 自动类型转换,数据类型从小到大,将会自动发生类型转换。
  • 强制类型转换,当数据类型从大到小,则需要强制转换,不能自动完成,可能会发生精度损失
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    //自动转换
    int a=100;
    long b=a; //从int到long,自动转换
    float c=0.1;
    double d=c; //从float到double,自动转换
    //强制转换
    int num = 100L; //会报错,int无法自动转换为long
    int num = (int)100L; //正确,实现了强制转换
    char s='a';
    a=(int)s; //正确,将字符串强制转化成int整数,将会输出a的ASCII码

ASCII码表

ASCII是使用7位二进制数(剩下的1位二进制为0)来表示所有的大写和小写字母,数字0 到9、标点符号,基本上所有的计算机语言都会用到ASCII码进行编码,JAVA也是如此,只不过java也是使用了Unicode编码,把ASCII码表放在这里便于查看

java编译器的一些优化

  • 对于byte,short,char三种类型,如果右侧赋值的数据大小没有超过范围,那么java编译器会自动加上(byte),(short),(char),例如:
    byte a = 30 <==> byte a = (byte) 30
  • 但是,当右侧赋值的数据大小超过范围后,java编译器则会直接报错
  • java中的加法默认将加数当作int数据类型,所以使用2个short类型的变量相加赋予另一个short类型时,编译器会报错,但是当short类型的右边赋予的是两个常量相加,那么编译器不会报错,而是会直接先将常量相加直接赋予short:
    1
    2
    3
    4
    short a=10;
    short b=20;
    short c=a+b; //会报错
    short c=10+20; //正确
    报错原因:

jshell的使用

jshell是java的交互式运行环境,类似于python的命令行的交互式运行环境,直接在命令提示符中输入jshell进入Java交互环境,之后直接输入一条java语句,便能直接输出语句结果,注意jshell中的语句不用以分号结尾。

选择结构

1.if语句
在Java程序中,如果要根据条件来决定是否执行某一段代码,就需要if语句。根据if的计算结果(true还是false),JVM决定是否执行if语句块(即花括号{}包含的所有语句)。

2.除了if语句外,还有一种条件判断,是根据某个表达式的结果,分别去执行不同的分支。switch语句根据switch (表达式)计算的结果,跳转到匹配的case结果,然后继续执行后续语句,直到遇到break结束执行。switch语句的基本语法是:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
switch (option) {
case 1:
// option=1时执行语句
break;
case 2:
// option=2时执行语句
break;
case 3:
// option=3时执行语句
break;
default:
// option都不符合时执行语句
break;
}

循环结构

循环语句就是让计算机根据条件做循环计算,在条件满足时继续循环,条件不满足时退出循环。
除了for,while外,Java还提供了一种for each循环,它可以更简单地遍历数组,和for循环相比,for each循环的变量n不再是计数器,而是直接对应到数组的每个元素。但是,for each循环无法指定遍历顺序,也无法获取数组的索引。它的用法如下所示:

1
2
3
4
int[] ns = { 1, 4, 9, 16, 25 };
for (int n : ns) {
System.out.println(n);
}

break和continue

break

在循环过程中,可以使用break语句跳出当前循环,break会跳出当前循环,当有多个嵌套的循环时,break只能跳出最内层的循环,然后剩下的循环都不会执行了。

continue

continue是提前结束本次循环,直接继续执行下次循环,在多层嵌套的循环中,continue语句同样是结束本次自己所在的循环。比如算从1-100的奇数的和时用continue就会很方便:

1
2
3
4
5
6
int sum=0;
for(int i=1;i<=100;i++)
{
if(i==1) continue;
sum+=i;
}

方法

方法的注意事项

  • 方法必须定义在类中,且不能在方法中再定义方法
  • 方法定义的前后顺序无所谓
  • 如果方法有返回值,那么必须加上return返回值,不能没有
  • 对于void方法,不能return加返回值,只能直接return;
  • 一个方法可以有多个return语句,但是必须保证能执行的只有一个

方法重载

在一个类中,我们可以定义多个方法。如果有一系列方法,它们的功能都是类似的,只有参数有所不同,那么,可以把这一组方法名做成同名方法,这种方法名相同,但各自的参数不同,称为方法重载(Overload),方法重载的目的是,功能类似的方法使用同一名字,更容易记住,因此,调用起来更简单。
例如,String类提供了多个重载方法indexOf(),可以查找子串:

  • int indexOf(int ch):根据字符的Unicode码查找;
  • int indexOf(String str):根据字符串查找;
  • int indexOf(int ch, int fromIndex):根据字符查找,但指定起始位置;
  • int indexOf(String str, int fromIndex)根据字符串查找,但指定起始位置。

重载的注意事项

1.方法的重载与下列因素相关:

  • 参数的个数不同
  • 参数的类型不同
  • 参数的多类型顺序不同
1
2
3
4
5
6
public static int sum(int x,int y);
public static int sum(int x,int y,int z);
public static int sum(double x,double y);
public static int sum(int x,double y);
puclic static int sum(double x,int y);
//以上正确,都能构成重载

2.方法的重载与下列因素无关:

  • 与参数的名称无关
  • 与方法的返回值类型无关
1
2
3
4
5
6
public static int sum(int x,int y);
public static int sun(int a,int b);
//以上不能构成重载会报错,因为与方法参数的名称无关
public static int sum(int x,int y);
public static double sum(int x,int y);
//以上也不能构成重载会报错,因为与方法的返回类型无关

数组

定义一个数组类型的变量,使用数组类型“类型[]”,有以下几种创建方法:

  • int[] a={1,2,3,4,5}; //根据{}之间的元素个数自动创建
  • int[] a=new int{1,2,3,4,5} //同上
  • int[] a=new int[5];a={1,2,3,4,5}; //先创建容量为5的数组,再赋值

数组的内存

1.当动态创建一个数组时,系统将会在栈区先写入数组名称,然后在堆区开辟空间存放数组元素,其中数组的索引也是存放在栈区的,当你要修改数组元素的值时,你首先将访问栈区的索引,然后这个索引再指向堆区对应的内存,如下图所示:

2.当一个数组赋值给另一个数组时:int[] arrayB = arrayA;这时候是将arrayA的地址直接赋值给arrayB了,此时arrayA和arrayB共用一个内存,本质上时同一个数组,其内存分配如下所示:

数组注意事项:

  • 数组所有元素初始化为默认值,整型都是0,浮点型是0.0,布尔型是false;
  • 要访问数组中的某一个元素,需要使用索引。数组索引从0开始
  • 可以修改数组中的某一个元素,使用赋值语句
  • 可以用数组变量.length获取数组大小
  • 数组是引用类型,在使用索引访问数组元素时,如果索引超出范围,运行时将报错
  • 直接输出数组名称将会得到数组的首地址
  • 数组的2个异常:超出数组索引(ArrayIndexOutOfBoundsException),空指针异常(NullPointerException):创建一个空数组时访问数组元素将会出现空指针异常

数组的使用

1.简单遍历输出
由于java提供了获取数组长度的方法(array.length),所以对于数组的遍历非常方便:

1
2
3
4
5
int[] array={1,2,3,4,5};
for(int i=0;i<array.length;i++)
{
System.out.println(array[i]);
}

2.查找数组最大值
以最大值为例,只需要用一个变量记录首元素,在遍历数组时,每次比较得出最大值更新这个变量即可

1
2
3
4
5
6
7
int[] array={6,5,4,3,2,1};
int max=array[0];
for(int i=1;i<array.length;i++)
{
if(max<array[i]) max=array[i];
}
System.out.println("最大值为:"+max);

3.数组的排序
对数组的排序中,常用的排序算法有冒泡排序、插入排序和快速排序等。以冒泡排序为例:

1
2
3
4
5
6
7
8
9
for (int i = 0; i < ns.length - 1; i++) {
for (int j = 0; j < ns.length - i - 1; j++) {
if (ns[j] > ns[j+1]) {
int tmp = ns[j];
ns[j] = ns[j+1];
ns[j+1] = tmp;
}
}
}

在java中,其标准库内已经内置了排序功能,我们也可以对它直接进行调用:

1
2
3
4
5
6
7
8
9
import java.util.Arrays;

public class Main {
public static void main(String[] args) {
int[] ns = { 28, 12, 89, 73, 65, 18, 96, 50, 8, 36 };
Arrays.sort(ns);
System.out.println(Arrays.toString(ns));
}
}

二维数组

二维数组可以理解为在一个数组中再定义数组,例如:

int[][] ns = {
        { 1, 2, 3, 4 },
        { 5, 6, 7, 8 },
        { 9, 10, 11, 12 }
    };

因为ns包含3个数组,所以ns的长度为3

二维数组的内存

二维数组在系统中的内存占用如下:

打印二维数组

打印二维数组可以用两层嵌套循环:

1
2
3
4
5
for (int[] arr : ns) {
for (int n : arr) {
System.out.print(n);
System.out.print(', ');
}

也可以直接使用Java标准库的Arrays.deepToString()直接打印二维数组。