Java基础中的基础

65 阅读14分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第20天,点击查看活动详情

因为不知道要从那里开始便想到什么就写什么吧(会按难易顺序来的🙃)

1、Java入门的专有名词

0. API

程序应用接口(Application Program Interface,API)也称为库。包含了为开发Java程序而预定义的类和接口。 我们平时所说的看API文档来学习,也就是说去看这些库文件所导出来的文档。
因为这方便我们去阅读类和接口里的方法的功能。

1. JVM

Java虚拟机(Java Virtual Machine,JVM)是运行 Java 字节码的虚拟机。 JVM 有针对不同系统的特定实现(Windows、Linux、macOS),目的是使用相同的字节码,它们都会给出相同的结果。`

2. JRE

Java程序的运行时环境(Java Runtime Environment,JRE) 其包含JVM和运行时所需要的核心类库。
我们想要运行一个已有的Java程序,那么只需安装JRE即可。
`因为想要生存就要有地方,所以JRE是基石

3. JDK

Java程序开发工具包(Java Development Kit,JDK)由一组独立程序构成,每个程序都是从命令行调用的,用于编译、运行和调试Java程序。 其包含JRE和开发人员使用的工具。
是Java程序开发工具包,包含JRE和开发人员使用的工具。

4. JDK、JRE和JVM的关系图 image.png 也就是说JDK包含有JRE(JVM + 核心类库) + 开发工具

5. Java程序开发运行流程(基于命令行窗口的实现)

image.png
1.编写程序(.class文件):可在记事本上完成
2.编译程序(.javac文件):利用javac命令来实现。通过javac编译之后,我们可以得到一个Java字节码文件
3.运行程序(.java文件):利用java命令来实现

PS:Javac是一个命令 —— 用于使用编译器,来生成对应的字节码文件,以便Java虚拟机运行。
因为计算机看不懂,所以要利用编译器来转换。

2、注释与命名习惯

程序设计风格(programming style)决定程序的外观。它便于让我们阅读与避免错误 1. 注释 注释:介绍每一个主要步骤并解释每个难以读懂之处 等之后回过头来复习的时候大有帮助

  • 行注释 -- //
  • 块注释 -- /* 注释内容 */
  • 文档注释 -- /** 文档内容 */

image.png

2. 命名习惯

严格遵循Java的命名习惯可以让你的程序易于理解,并且能避免错误

  • 变量和方法:小写第一个单词,在所有后续档次中大写第一个字母
  • 常量:将所有字母大写
  • 类名:将每个名称中的第一个字母大写
  • 包名:将所有字母小写

image.png PS:命名规范并不强制要求,这只是建议而已。(等你习惯了,再去看官方文档便很容易看懂该名称是什么含义与作用😜)

3、程序设计错误

1. 语法错误

在编译过程中由编译器检测出来的错误称为语法错误(syntax error)或编译错误(compile error) 语法错误是由创建代码时的错误引起的

// 以下代码那里错了?
int i = 666
System.out.println("i = " + i);

2. 运行时错误

运行时错误(runtime error)是引起程序非正常终止的错误。

// 以下代码那里错了?
System.out.println(1 / 0);

运行应用程序时,当环境检测到一个不可能执行的操作时,就会出现运行时错误。(如:除零错误)

3. 逻辑错误

当程序没有按预期的方式执行时就会发生逻辑错误(logic error)

// 以下代码输出多少? 逻辑上有问题吗?
// 修改前:
System.out.println(9 / 5);

// Outer:1
// Expecte:1.8

// 修改后:
System.out.println(90 / 5);

通常情况下,因为编译器可以明确指出错误的位置以及出错的原因,所以语法错误时很容易发现和纠正的。运行时错误也不难找,因为在程序异常终止时,错误的原因和位置都会显示在控制台上。然后,查找逻辑错误就很有挑战性了 (这很有意思😊)

4、标识符

  • 标识符是由字符、数字、下划线(_)和美元符号($)构成的字符序列
  • 标识符必须与字母、下划线(_)或美元符号($)开头,不能以数字开头
  • 标识符不能是保留字(关键字,有50个)
  • 标识符不能是true、false或null
  • 标识符可以为任意长度 PS:Java是区分大小写的
以下变量名哪些是正确的?
game、Test、a++、--a、$4、#44classint、radius

a++、--a、#44classint不是正确的,其他都是
标识符不包含 +、-、#,还有关键字

5、变量与常量

用于表示在程序中可能被改变的值 变量 变量,就是指会变的值;而常量就是指不会变的值

变量与常量的赋值形式

final int VALUE0 = 5; // 常量 该值不会被改变
int value1 = 6;
int value2 = 7, value3 = 4;

int value4, value5; // 声明变量而未初始化;但有初始值(默认值)
value4 = 9;

变量声明通知编译器根据数据类型为变量分配合适的内存空间

方法中声明的变量在使用之前必须被赋值

// 以下代码有什么错?
int valueThree;
System.out.println(valueThree); // 方法中声明的变量在使用之前必须被赋值

变量的注意事项:

   ①名字不能重复

   ②变量未赋值,不能使用

   ③long类型的变量定义的时候,为了防止整数过大,后面要加L/l

   ④float类型的变量定义的时候,为了防止类型不兼容,后面要加F/f

long l = 10_000_000_000L;// 为了提高与毒性,Java运行在一个数值型字面值(值)得两个数字只讲使用下划线(_)
System.out.println(l);
// PS:当去掉L或l时,报编译异常

6、数据类型

Java语言是强类型语言,对于每一种数据都给出了明确的数据类型,不同的数据类型也分配了不同的内存空间,所以它们表示的数据大小也是不一样的。

Java的数据类型共有两大类
基本数据类型、引用数据类型

image.png

Java的基本数据类型有:

  • 整型(byte、short、int、long)
  • 浮点型(float、double)
  • 字符型(char)
  • 布尔型(boolean) 共 四类八种

image.png

7、赋值语句

赋值语句将一个值指定给一个变量。在Java中赋值语句可以作为一个表达式。

要给变量赋值,变量名必须在赋值操作符的左边。
在Java中,赋值语句本质上就是一个表达式,该表达式的值是赋给赋值操作符左边变量的值 (将右边的值赋给左边 ==> 右结合性)
由于这个原因,赋值语句也称为赋值表达式(assignment expression)

// 以下代码输出的形式是什么?
int i, j, k;
i = j = k = 1;
System.out.println("i = " + i + "j = " + j + "k = " + k);

// 以下代码正确嘛?
int x = y = 1;
//Outer:无声明 y 变量,编译出异常

8、运算符

0. 增强赋值运算符

运算符 +、-、*、/、%可以结合赋值运算符形成增强运算符

image.png

PS:增强赋值运算符在表达式中所有其他运算符计算完成后执行

int x = 0;
x += 4 + 5.5 * 1.5; // 等价于 x = x + (4 + 5.5 * 1.5);
System.out.println("x = " + x); // Outer:x = 12

注意:在增强运算符中是没有空格的!

1. 自增、自减运算符

自增运算符(++)和自减运算符(--)对变量进行加1和减1操作 ++和--是对变量进行自增1和自减1的简写运算符

注意事项:

  • ++、-- 只能操作变量,不能操作字面量(即数值)
  • ++、-- 既可以放在变量的后边,也可以放在变量的前边,如:i++; 或++i;

PS:++运算符与--运算符的使用是一样的,只是作用不一样罢了。以下将以++运算符为例进行讲解

单独使用时, ++和-- 无论是放在变量的前边还是后边,结果是一样的。

int i = 10;
i++; // 单独使用,相当于 i=i+1;
System.out.println("i = " + i); // Outer:i = 11

int j = 10;
++j; // 单独使用,相当于 j=j+1;
System.out.println("j = " + j); // Outer:j = 11

自增(++)和自减(--)运算符如果不是单独使用(即参与运算),放在变量前后会存在明显区别。

运算符(++或--)放在变量的前面,先对变量的值进行自增或自减运算,再用变量的值进行其它运算。

int m = 10;
int n = ++m; // 赋值运算,++在前边,m先进行自增运算,再将值赋值给n
System.out.println("m = " + m + ", n = " + n); // m = 11,n = 11

运算符(++或--)放在变量的后面,先用变量的值进行其它运算,再对变量的值进行自增或自减运算。

int x = 10;
int y = x++; // 赋值运算,++在后边,x的值先赋值给y,x再进行自增运算
System.out.println("x = " + x + ", y = " + y); // x = 11,y = 10

总结: int j = ++i; 由于i离得较远,所以先自增,再赋值 int j = i++; 由于i离得较近,所以先赋值,再自增

double x = 1.0;
double y = 5.0;
double z = x-- + (++y);
System.out.println("x = " + x + ", y = " + y + ", z = " + z);
//Outer: x = 0.0, y = 6.0, z = 7.0

2. 逻辑运算符

9、类型转换

1. 自动类型转换

把一个表示数据范围小的数值或者变量赋值给另一个表示数据范围大的变量

image.png PS: byte 和 short不能与char进行数据类型转换

总是可以将一个数值赋给支持更大数值范围的类型的变量。
但是,如果不进行类型转换,就不能将一个值赋给范围较小的类型的变量。

2. 强制类型转换

把一个表示数据范围大的数值或者变量赋值给另一个表示数据范围小的变量

注意:
Ⅰ.类型转换不改变被转换的变量

double d = 4.5;
int i = (int) d; // d的值并未被改变
System.out.println("d = " + d + ", i = " + i);
// Outer:d = 4.5, i = 4

Ⅱ.Java中,x1 op= x2 形式的增强赋值表达式实现为 x1 = (T)(x1 op x2), 这里T是x1的类型

int sum = 0;
sum += 4.5; // 等价于 sum = (int)sum + 4.5;
System.out.println("sum = " + sum);
// Outer: sum = 4
// 因为扩展的赋值运算符的底层帮我们进行强制类型转换

总结:
自动类型转换 ==> 表示范围小的赋给表示范围大的(小转大)
强制类型转换 ==> 表示范围大得赋给表示范围小的(大转小)需要告知对方 (我的很大,你忍一下~😤)

10、练习题

1. 将营业税值显示为小数点后两位

Scanner sc = new Scanner(System.in);

System.out.print("Enter purchase amount:");
double purchaseAmount = sc.nextDouble();

double tax = purchaseAmount * 0.06;
System.out.println("Sales tax is $" + (int) (tax * 100) / 100.0);
// 先将tax乘于100 再转型为int类型 之后再除于100.0使得其保留至多两位小数

/*
purchaseAmount == 197.55
tax = 197.55 * 0.06 == 11.853
转换过程: 11.8535 --> 1185.3 --> 1185 --> 11.85
Outer:
    Enter purchase amount:197.55
    Sales tax is $11.85
 */

2. 输入一个两个数的整数,分别输出该整数的个位数与十位数

Scanner sc = new Scanner(System.in);
System.out.print("Enter the int number:");
int value = sc.nextInt();
int ge = value % 10;
int shi = value / 10 % 10;
System.out.println("shi is:" + shi + ", ge is:" + ge);

/*
Outer:
    Enter the int number:78
    shi is:7, ge is:8
    
总结: 
    个位数值: value % 10
    十位数值: value / 10 % 10
    百位数值: value / 100 % 10
由此可得通用公式
    某位数值: value / 某 % 10
 */

3. 显示当前时间

long totalMilliseconds = System.currentTimeMillis(); // 获取从1970-1-1到现在的毫秒值

long totalSeconds = totalMilliseconds / 1000;  // 获取总的秒数

long currentSecond = totalSeconds % 60; // 得到当前的秒数

long totalMinutes = totalSeconds / 60; // 获取总的分钟数

long currentMinute = totalMinutes % 60; // 得到当前分钟数

long totalHours = totalMinutes / 60; // 获取总的小时数

long currentHour = totalHours % 24; // 得到当前小时数

System.out.println("Current time is " + (currentHour + 8) + ":" + currentMinute + ":" + currentSecond + " GMT+8");

11、流程控制

1. 顺序结构

顺序结构的程序设计是最简单的,只要按照解决问题的顺序写出相应的语句就行,它的执行顺序是自上而下,依次执行。

2. 选择(分支)结构

  1. 单分支if语句

指当且仅当条件为true时执行一个动作

单分支if语句:
        if(布尔表达式){
            语句(组);
        }
  1. 双分支if-else语句

if-else语句根据条件时真或者是假,决定执行的路径

双分支if-else语句:
        if(布尔表达式){
            布尔表达式为真时执行的语句(组;
        }
        else{
            布尔表达式为假时执行的语句(组;
        }
  1. 嵌套的if语句和多分支if-else语句

if语句可以在另一个if语句中,形成嵌套的if语句

嵌套的if语句和多分支if-else语句:
        if(布尔表达式1){
            布尔表达式1为真时执行的语句(组);
        }
        else if(布尔表达式2){
            布尔表达式1为假时,且布尔表达式2为真所执行的语句(组);
        }
        ...
        else if(布尔表达式n){
                布尔表达式1为假时,且布尔表达式2为假,...,且布尔表达式n为真时所执行的语句(组);
        }else{
            均不符合以上布尔表达式时,所执行的语句(组);
        }
  1. switch语句

switch语句基于变量或者表达式的值来执行语句

switch语句:
switch(switch表达式){
    case 值1: 语句(组)1;
            break;
    case 值2: 语句(组)2;
            break;
...
    case 值n: 语句(组)n;
            break;
    default: 默认情况下执行的语句(组)
}

switch规则:

  1. switch表达式必须能计算出一个char、byte、short、int或者String类型的值,并且必须用括号括住!(PS:之后它还可以接收一个枚举类型的值)简的来说,switch表达式需要传入一个字符类型或整数类型或枚举类型的数据
  2. 值1,值2,...,值n:必须与switch表达式的值具有相同的数据类型。注意:值1,值2,...,值n都是常量表达式!
  3. 当switch表达式的值与case语句的值相匹配的时候,执行从该case语句开始,直到遇到第一个break语句或到达该switch语句的末尾才结束该switch。
  4. 默认情况(default)是可选的,当没有一个case与switch表达式相匹配的时候,则执行该操作。
  5. 关键字break是可选的。break语句会立即终止switch语句。

3. 循环结构

  1. while循环

while循环在条件为真的情况下,重复地执行语句。

while(循环继续条件){
    // 循环体
    语句(组);
}
  1. do-while循环

do-while循环和while循环基本一样,不同的是它是先执行循环体一次,然后再去判断循环继续条件

do{
    // 循环体
    语句(组);
}while(循环继续条件);
  1. for循环

for循环具有编写循环的简明语法

for(初始操作;循环继续条件;每次迭代后的操作){
    // 循环体
    语句(组);
}

初始操作 ==> 初始化
循环继续条件 ==> 条件
每次迭代后的操作 ==> 循环变量

  1. 三种循环的区别:

for循环和while循环先判断条件是否成立,然后决定是否执行循环体(先判断后执行)
do...while循环先执行一次循环体,然后判断条件是否成立,是否继续执行循环体(先执行后判断)

  1. for和while的区别:

条件控制语句所控制的自增变量,因为归属for循环的语法结构中,在for循环结束后,就不能再次被访问到了(自增变量是定义在for循环里的属于局部变量,用完就被释放掉了
条件控制语句所控制的自增变量,对于while循环来说不归属其语法结构中,在while循环结束后,该变量还可以继续使用。 PS:让我吐槽下,这个流程控制模块的基础知识真难写...🤧所以接下来准备写学习基础知识容易掉的坑(点)了🙇‍♂️


以下将写一些可能平常不太注意的点,或疑惑的点。🙇‍♀️

12、数组

数组(array)是一种用于存储多个相同类型数据的存储模型

  1. 数组的背景知识

一旦数组被创建,它的大小是固定的。
可以使用一个数组引用变量和下标来访问数组中的元素(arrayName[index])。

数组的创建方式:

1. doulbe[] myList = new double[10];
2. double[] myList;
   myList = new double[10];

数组的初始化方式:

1. doulbe[] myList = {1.0, 1.1, ..., 2.0}; // 动态初始化
2. doulbe[] myList = new double[10]; // 静态初始化
   myList[0] = 1.0;
   myList[1] = 1.1;
   ...
   myList[9] = 2.0;

二维数组的访问:

二维数组里的每一个元素都是一个一维数组的数组。(即,二维数组的元素存储着一维数组的引用
二维数组的长度 = 数组中所有元素的总和(即,二维数组的元素总和 * 一个二维数组中所存储的一维数组的元素个数的总和)

doulbe[][] myList = new double[2][3];
由上述数组可知:
    二维数组个数有2个
    一维数组个数有3个
myList[index1]:取得index1下标下的二维数组元素(0 <= index1 <= 2)
myList[index1][index2]:去index1下标下的二维数组元素中取index2下标下的一维数组元素(0 <= index2 <= 3)
  1. 数组的动态初始化 动态初始化:初始化时只指定数组长度,由系统为数组分配初始值。

image.png

  1. 数组的静态初始化 静态初始化:初始化时指定每个数组元素的初始值,由系统决定数组长度。

image.png

3.数组操作的两个常见小问题

索引越界异常:访问了数组中不存在的索引,所造成索引越界问题。
空指针异常:访问的数组已经不再指向堆内存的数据,造成空指针异常。
null:空值,引用类型的默认值,表示不指向任何有效对象。

13、方法

方法(method)是将具有独立功能的代码块组织成为一个整体,使其具有特殊功能的代码集。

注意:

  1. 方法必须先创建才可以使用,该过程称为方法定义

image.png

  1. 方法创建后并不是直接运行的,需要手动使用后才执行,该过程称为方法调用

image.png

  1. 形参实参的区别 形参:方法定义中的参数
    实参:方法调用中的参数

  2. 成员变量局部变量

  • 成员变量类中 方法外的变量
  • 局部变量方法中的变量

image.png

  1. 方法重载

方法重载仅对应方法的定义,与方法的调用无关 重载仅针对同一个类中方法的名称参数进行识别,与返回值无关。换句话说 不能通过返回值来判定两个方法是否相互构成重载 ==> 即方法类型也与其无关

14、对象和类

类是对现实生活中一类具有共同属性和行为的事物的抽象。
对象是能够看得到,摸的着的真实存在的实体
类为对象定义了属性和行为。类是定义相同类型对象的结构(模板)。

  1. 类的组成:属性行为
  • 属性:在类中通过成员变量来体现(类中方法外的变量
  • 行为:在类中通过成员方法来体现(没有被static关键字修饰的方法
  1. 对象的属性行为
  • 属性:对象具有的各种特征,每个对象的每个属性都拥有特定的值。
  • 行为:对象能够执行的操作。

15、this 关键字

①  this修饰的变量用于指代成员变量(类中 方法外的变量)

  • 方法的形参如果与成员变量同名,不带this修饰的变量指的是形参,而不是成员变量。
  • 方法的形参没有与成员变量同名,不带this修饰的变量指的是成员变量。

② 什么时候使用this呢?

  • 解决局部变量隐藏成员变量(即 变量同名问题)

③  this:代表所在类的对象的引用

总结:

  • this的用处就是:哪个对象调用,this就代表哪个对象(谁调用我,我就是谁

16、封装

面向对象三大特征之一(封装,继承,多态)
是面向对象编程语言对客观世界的模拟,客观世界里成员变量都是隐藏在对象内部的,外界是无法直接操作的。 java的封装性即是信息隐藏,把对象的属性和行为结合成一个相同的独立单体,并尽可能地隐藏对象的内部细节

封装的特性是对属性来讲的。

封装的目标就是要实现软件部件的"高内聚,低耦合",防止程序相互依赖带来的变动影响。

  • 高内聚(就是类的内部数据操作细节自己完成,不允许外部干涉)
  • 低耦合(仅暴露少量的方法给外部使用)

在面向对象的编程语言中,对象是封装的基本单位,面向对象的封装比传统语言的封装更清晰,有力。

注:一般来说,只要是属性,就必须封装,java中通过将属性声明为私有的(private) ,再通过公共的(public) ,以及getter和setter方法设置和获取,实现对属性的操作。

17、继承

子类继承父类,表明子类是一种特殊的父类,并且具有父类所不具有的一些属性或方法。

类的继承格式:

class 父类 {
}

class 子类 extends 父类 {
} 

继承的特性:

  • 子类拥有父类非 private 的属性、方法
  • 子类可以拥有自己的属性和方法,即子类可以对父类进行扩展
  • 子类可以用自己的方式实现父类的方法
  • Java 的继承是单继承,但是可以多重继承,单继承就是一个子类只能继承一个父类,多重继承就是,例如 A类继承B类,B类继承C类,所以按照关系就是 C类是B类的父类,B类是A类的父类,这是 java继承区别于C++继承的一个特性
  • 提高了类之间的耦合性(继承的缺点,耦合度高就会造成代码之间的联系)

18、多态

多态简单来讲就是:事物在运行过程中所呈现的不同状态。

需要满足以下关系:

  1. 要有继承关系
  2. 子类要重写父类的方法
  3. 父类引用指向子类

后话:
JavaSE 写的...真的很痛苦...
因为JavaSE其实真要写每个知识点都能分一个章节的...🙄
就以前面的面向对象的三大特性来说,他们这三个拆开来至少每个要讲清楚没个万字搞不定的...我这里为了浓缩...只好精简了...
希望之后有空能把这三大特性拆分细讲吧...
强烈推荐去看我写的《内部类》那时候用了我两三天的时间,写的很详细,至少我感觉该讲的点都讲了orz 感觉还可以的话,点个免费的赞👍再走呗