Java学习笔记
Java的学习笔记,一些很基础的东西。(Hello --> 接口)
# Hello World
梦开始的地方
package com.company;
public class Main {
public static void main(String[] args) {
System.out.println("hello world");
}
}
2
3
4
5
6
7
- 主类名一定要与文件名保持一致,否则会编译失败。
public static void main(String[] args)
类似于C语言的int main()
,是程序的入口,其格式固定。- 调用
System.out.println
方法,输出"Hello World"。
# IDEA项目结构
# 方法
# 简单方法
public static void 方法名称(){
方法体
}
//调用格式:方法名称();
2
3
4
5
如以下代码打印一个5*5的正方形:
public class Main {
public static void main(String[] args) {
sqrt();
}
public static void sqrt() {
for (int i = 0; i < 5; i++) {
for (int j = 0; j < 5; j++) {
System.out.print('*');
}
System.out.println(); //println()自动换行
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
运行结果:
# 方法的定义
- 方法:一段用来完成特定功能的代码片段,类似于C语言的函数,也有参数和返回值。
- 参数:传入方法的数据。
- 返回值:从方法中返回的数据。
定义方法完整的格式:
修饰符 返回值类型 方法名称(参数类型 参数名称, ...){
方法体
return 返回值;
}
2
3
4
- 方法名称要符合小驼峰命名法
下面是一段求两数字之和代码:
public class Main {
public static void main(String[] args) {
System.out.println(sum(10,5));
}
public static int sum(int a, int b) {
return a + b;
}
}
2
3
4
5
6
7
8
9
运行结果:
# 方法的三种调用格式
- 单独调用:方法名称(参数);
- 打印调用:System.out.println(方法名称(参数));
- 赋值调用:res = 方法名称(参数);
# 方法的简单练习题目
# 比较两数是否相同
public class Main {
public static void main(String[] args) {
boolean res = compNum(10, 9);
if (res == true) {
System.out.println("两数字相同");
} else {
System.out.println("两数字不同");
}
}
public static boolean compNum(int a, int b) {
boolean res;
if (a == b) {
res = true;
} else {
res = false;
}
return res;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 求1-100的和
public class Main {
public static void main(String[] args) {
System.out.println(sumNum());
}
public static int sumNum() {
int res = 0;
for (int i = 1; i <= 100 ;i++) {
res += i;
}
return res;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
这些题目都比较简单,目的是掌握基本的方法定义以及调用方式。
# 方法的注意事项
- 方法应该定义在类当中,不能在方法中再定义方法。
- 方法定义的前后顺序无所谓。
- 方法定义后不会执行,如果希望执行,则一定要进行调用。
- 如果方法有返回值,一定要
return 返回值
。 - 返回值类型一定要与定义方法时的返回值类型一致。
- 一个方法中可以有多个return语句,但是只有一个可以被执行到,执行的同时,退出方法。
# 方法的重载(overload)
- 对于功能类似的方法来说,因为参数列表不一样,则需要定义多个不同的方法,在调用的时候就比较麻烦,这时候就可以使用方法的重载。
- 方法的重载:多个方法名称一样,但是参数列表不一样。
下面一段代码来进行示例:
public class Main {
public static void main(String[] args) {
System.out.println(sum(1, 2));
System.out.println(sum(1, 2, 3));
System.out.println(sum(1, 2, 3, 4));
}
public static int sum(int a, int b) {
return a + b;
}
public static int sum(int a, int b, int c) {
return a + b + c;
}
public static int sum(int a, int b, int c, int d) {
return a + b + c + d;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
方法重载与下列因素相关:
- 参数个数不同
- 参数类型不同
- 参数多类型顺序不同
第1、2点都好理解,关于第三点:
public static double sum(int a, double b) {
return a + b;
} //先int 后double
public static double sum(double a, int b) {
return a + b;
} //先double 后int
2
3
4
5
6
7
方法重载与下列因素无关:
- 与参数名称无关
- 与方法的返回值类型无关
# 数组
- 数组是一种引用数据类型。
- 数组中的多个数据,类型必须统一。
- 数组的长度在运行过程中不可改变。
# 数组的初始化
- 在内存中创建一个数组,并向其中赋予一些默认值。
两种常见的初始化方法:
- 动态初始化(指定长度)
- 静态初始化(指定内容)
如果不确定数组当中的具体内容,则使用动态初始化,否侧,已经确定了具体的内容,则使用静态初始化。
# 动态初始化数组
数据类型[] 数组名称 = new 数据类型[数组长度];
- 左侧的数据类型,代表数组中存放的数据的数据类型
- 左侧的中括号,代表我是一个数组
- 左侧的数组名称,给数组取一个名字
- 右侧的new,代表创建数组的动作
- 右侧的数据类型要与左侧的数据类型保持一致
- 右侧的中括号中的数组长度代表数组可以存放多少个数据
//创建一个存放100个整数的数组
int[] arrayA = new int[100];
//创建一个存放10个字符串的数组
String[] arrayB = new String[10];
2
3
4
5
动态化初始数组时,其中元素都会有一个默认值,其规则如下:
数据类型 | 默认值 |
---|---|
int | 0 |
double | 0.0 |
char | '\u0000' (unicode编码) |
boolean | false |
引用类型 | NULL |
# 静态初始化数组
数据类型[] 数组名称 = new 数据类型[] {元素1,元素2,...};
//省略格式
数据类型[] 数组名称 = {元素1,元素2,...};
2
3
4
int [] arrayA = new int[] {1, 2, 3, 4, 5};
//省略格式
int [] arrayA = {1,2,3,4,5};
2
3
4
- 虽然静态初始化没有指定长度,但是并不代表数组没有长度,可以通过后面的元素个数自动推算长度。
# 数组元素的访问
- 直接访问数组名称,得到的是数组对应的内存地址的哈希值。
- 访问数组元素的格式:数组名[索引值]。
- 索引值从0开始,一直到“数组的长度-1”为止。
由于和C语言类似,这里就不上代码了,我是懒🐕。
# JAVA中的内存划分
JAVA的内存需要划分5个部分:
栈(Stack):存放的都是方法中的局部变量,方法的运行一定要在栈当中。
- 局部变量:方法的参数,或者是方法内部的变量。
- 作用域:一旦超出作用域,立刻从栈内存当中消失。
堆(Heap):凡是new出来的东西,都在堆当中。
- 堆内存里面的东西都有一个地址值:16进制。
- 堆里面的数据,都有默认值,其规则见动态初始化数组。
方法区(Method Area):存储.class相关信息,包含方法的信息。
- 存储的是方法的死信息,方法运行需要在栈当中。
本地方法栈(Native Method Stack):与操作系统相关。
寄存器(Pc Register):与CPU相关。
# 一个数组的内存图
public class Main {
public static void main(String[] args) {
int[] arrayA = new int[3];
System.out.println(arrayA);
System.out.println(arrayA[0]);
System.out.println(arrayA[1]);
System.out.println(arrayA[2]);
//改变数组元素的值
arrayA[1] = 10;
arrayA[2] = 20;
System.out.println(arrayA);
System.out.println(arrayA[0]);
System.out.println(arrayA[1]);
System.out.println(arrayA[2]);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
在内存中的存储方式:
# 一些常见异常
# 索引值越界异常
和C语言类似,如果数组索引值超过数组最大值,则会抛出异常:
ArrayIndexOutOfBoundsException
int[] array = new int[2];
System.out.println(array[2]);
2
# 空指针异常
所有的引用类型变量都可以赋值为null,代表其中什么都没有。
数组必须new初始化之后才可以使用其中的元素,如果只是赋值了一个null,没有进行new创建,则会发生空指针异常:NullPointerException。
public class Main {
public static void main(String[] args) {
int[] arrayA = null;
System.out.println(arrayA[0]);
}
}
2
3
4
5
6
# 数组里的一些基本操作
# 获取数组的长度
数组名.length
public class Main {
public static void main(String[] args) {
int[] arrayA = {1,2,3,5,6,8,9,0,10,11,12,13};
System.out.println(arrayA.length);
}
}
2
3
4
5
6
7
# 数组的遍历
for (int i = 0, i < array.length , i++) {
System.out.println(array[i]);
}
2
3
# 求数组的最值
public class Main {
public static void main(String[] args) {
int[] arrayA = {1, 2, 3, 5, 6, 8, 9, 0, 10, 11, 12, 13};
int max = arrayA[0];
for (int i = 0; i < arrayA.length; i++) {
if (arrayA[i] > max) {
max = arrayA[i];
}
}
System.out.println("最大值是:"+max);
}
}
2
3
4
5
6
7
8
9
10
11
12
求最小值类似
# 数组元素的反转
public class Main {
public static void main(String[] args) {
int[] arrayA = {1, 2, 3, 4, 5};
for (int i = 0; i < (arrayA.length / 2); i++) {
int temp = arrayA[i];
arrayA[i] = arrayA[(arrayA.length - 1) - i];
arrayA[(arrayA.length - 1) - i] = temp;
}
for (int i = 0; i < arrayA.length; i++) {
System.out.print(arrayA[i]+" ");
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
# 数组与方法相关
# 数组作为方法参数_传递地址
public class Main {
public static void main(String[] args) {
int[] array = {1, 2, 3, 4, 5};
arrayP(array);
}
public static void arrayP(int[] array) {
for (int i = 0; i < array.length; i++) {
System.out.println(array[i]);
}
}
}
2
3
4
5
6
7
8
9
10
11
12
当数组作为方法的参数的时候,向方法中传递的其实是数组的地址值。
# 数组作为方法的返回值_返回地址
public class Main {
public static void main(String[] args) {
int[] array = arrayR();
System.out.println(array[0]);
System.out.println(array[1]);
}
public static int[] arrayR() {
int sum = 5 + 5;
int avg = (5 + 5) / 2;
int[] res = {sum, avg};
return res;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
当数组作为方法返回值的时候,返回的是数组的地址,然后在main方法中新建一个数组array用来接受返回的地址值。
# 面向对象基础
# 面向对象思想概述
面向过程:当要实现某一个功能的时候,每一个具体的步骤都要亲力亲为,详细处理每一个细节。
面向对象:当要实现某一个功能的时候,不关心具体步骤,而是要找一个具有该功能的方法,来帮助我们做事。
以打印数组中每一个元素为例:
import java.util.Arrays;
public class Main {
public static void main(String[] args) {
int[] arrayA = {1, 2, 3, 4, 5};
//面向过程
System.out.print("[");
for (int i = 0; i < arrayA.length; i++) {
if (i < arrayA.length - 1) {
System.out.print(arrayA[i] + ", ");
} else {
System.out.print(arrayA[i] + "]");
}
}
System.out.println();
System.out.println("------------------");
//面向对象
//找一个JDK给我们提供的Arrays类
//使用其中的toString方法
System.out.print(Arrays.toString(arrayA));
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 类和对象
类:是一组相关属性和行为的集合,可以看成是一类事物的模板,使用事物的属性特征和行为特征来描述该类事物。
现实中,描述一类事物:
- 属性:就是该事物的状态信息。
- 行为:就是该事物能够做什么。
举例:小猫
- 属性:名字,体重,年龄,颜色。
- 行为:走,跑,叫,吃饭,······
对象:是一类事物的具体实现,对象是类的一个实例,具有类该事物的属性和行为。
- 类是对一类事物的描述,是抽象的。
- 对象是对一类事物的实例,是具象的。
- 类是对象的模板,对象是类的实例。
# 类的定义
//定义一个学生类
public class Student {
//成员变量(属性)
String name;
int age;
//成员方法(行为)
public void eat() {
System.out.println("吃饭");
}
public void study() {
System.out.println("学习");
}
public void sleep() {
System.out.println("睡觉");
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
注意事项:
- 成员变量是直接定义在类当中的。
- 成员方法不要写static关键字。
通常情况下,类不能直接使用,需要根据类创建一个对象才能使用
- 导包(import 包名称.类名称):就是指出所使用的类在什么位置。对于和当前类属于同一个包的情况,可以省略包名称不写。
- 创建:
类名称 对象名 = new 类名称();
- 使用:
- 使用成员变量:对象名.成员变量名
- 使用成员方法:对象名.成员方法名(参数)
例如,在com.company包下有两个类:Main和Student
//Student类
public class Student {
//成员变量(属性)
String name;
int age;
//成员方法(行为)
public void eat() {
System.out.println("吃饭");
}
public void study() {
System.out.println("学习");
}
public void sleep() {
System.out.println("睡觉");
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
在Main类中new一个Student类的对象,并且调用成员方法:
import com.company.Student; //导入类
public class Main {
public static void main(String[] args) {
Student stu = new Student();
stu.name = "F4de";
stu.age = 20;
System.out.println("姓名:"+stu.name);
System.out.println("年龄:"+stu.age);
stu.eat();
stu.study();
stu.sleep();
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
# 对象与方法相关
# 对象类型作为方法的参数
public class Main {
public static void main(String[] args) {
Student stu = new Student();
stu.name = "F4de";
stu.age = 20;
stuP(stu);
}
public static void stuP(Student param) {
System.out.println(param.name);
System.out.println(param.age);
System.out.println(param);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
同数组一样,传入方法的是对象的地址值
com.company.Student
代表该对象是在com.company包中的Student类的一个对象。1b6d3586
代表该对象的地址值。
# 对象类型作为方法的返回值
public class Main {
public static void main(String[] args) {
Student param = stuR();
System.out.println(param.name);
System.out.println(param.age);
System.out.println(param);
}
public static Student stuR() {
Student stu = new Student();
System.out.println(stu);
System.out.println("-----------");
stu.name = "F4de";
stu.age = 20;
return stu;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
对象作为方法的返回值,返回的仍是对象的地址值。
# 局部变量和成员变量的区别
定义的位置不一样:
- 局部变量:定义在方法的内部
- 成员变量:定义在方法的外部,直接写在类当中
作用范围不一样:
- 局部变量:只有方法当中才可以使用,出了方法就不能再用
- 成员变量:整个类中都可以使用
默认值不一样:
- 局部变量:没有默认值,如果想要使用,必须手动进行赋值。
- 成员变量:如果没有赋值,则会有默认值
在内存中的位置不一样:
- 局部变量:位于栈内存
- 成员变量:位于堆内存
生命周期不一样:
- 局部变量:随着方法进栈而诞生,随着方法出栈而消失
- 成员变量:随着对象的创建而诞生,随着对象的回收而消失
# 面向对象的封装性
封装在JAVA中的体现:
- 方法就是一种封装
- 关键字private也是一种封装
# 方法的封装
比如,我们可以用下面的代码进行一个求数组中元素最大值功能的封装:
public class Main {
public static void main(String[] args) {
int[] arrayA = {1,2,3,4,5};
System.out.println(getMax(arrayA));
double[] arrayB = {0.1,0.3,0.4,0.9,1.2};
System.out.println(getMax(arrayB));
}
public static int getMax(int[] array) {
int max = array[0];
for (int i = 0; i < array.length; i++) {
if (array[i] > max) {
max = array[i];
}
}
return max;
}
//重载getMax方法
public static double getMax(double[] array) {
double max = array[0];
for (int i = 0; i < array.length; i++) {
if (array[i] > max) {
max = array[i];
}
}
return max;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# private关键字的封装
使用private关键字可以提高我们代码的安全性,使用private关键字修饰某个变量的时候,该变量就只能在本类中访问:
package com.company;
public class Student {
String name;
private int age;
}
2
3
4
5
6
package com.company;
public class Main {
public static void main(String[] args) {
Student stu = new Student();
stu.name = "F4de";
stu.age = "20";
}
}
2
3
4
5
6
7
8
9
10
使用该关键字可以提高代码的安全性,当我们输入stu对象的年龄为一个不合理值的时候,比如-10,如果不使用private关键字修饰age,则-10会直接赋值给age,虽然此时可以通过if语句来输出字符串,来提示这种不合理的情况,但是此时age的值就已经是-10了,从而造成了一种“垃圾值”,但是如果使用该关键字修饰age,则可以避免这种情况:
//Main类
package com.company;
public class Main {
public static void main(String[] args) {
Student stu = new Student();
stu.name = "F4de";
//通过setAge()方法间接访问
stu.setAge(-10);
System.out.println(stu.getAge());
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
package com.company;
public class Student {
String name;
private int age;
//虽然age在Main类中无法访问,但是仍可以在本类中访问
//通过setAge()方法,从而达到间接访问的目的
//注意,这个方法名称必须是'set变量名()',其中变量名首字母大写
//返回值类型必须是void
//通常称这种方法为Setter方法
public void setAge(int num) {
if (num < 0 || num > 130) {
System.out.println("数据不合理");
} else {
age = num;
}
}
//getAge()方法,规则也是一样的
//必须有返回值,返回值类型与被修饰的成员变量类型相同
//通常称这种方法为Getter方法
public int getAge() {
return age;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
可以看到,age的值仍然是0,并没有被-10这个“垃圾值”所污染,这样一来,代码的安全性就会有很大提高。
# this关键字
当方法的局部变量和类的成员变量重名的时候,根据“就近原则”,优先使用局部变量。如果需要访问本类中的成员变量,需要使用this.成员变量名
这种格式
package com.company;
public class Main {
public static void main(String[] args) {
Hello person = new Hello();
person.name = "张三";
person.hello("李四");
}
}
2
3
4
5
6
7
8
9
ackage com.company;
public class Hello {
String name;
public void hello(String name) {
System.out.println(name + "你好,我是" + name);
//输出 “李四你好,我是李四”
System.out.println(name + "你好,我是" + this.name);
//输出 “李四你好,我是张三”
}
}
2
3
4
5
6
7
8
9
10
11
12
13
- 通过谁调用的该方法,谁就是this
比如上面这个例子,在Main类中person对象调用了hello()方法,那么hello()方法中的this就是person,this.name
就是person.name
。
可以通过下面的代码进行证明:
package com.company;
public class Main {
public static void main(String[] args) {
Hello person = new Hello();
person.name = "张三";
person.hello("李四");
System.out.println("person对象的地址:"+person);
}
}
2
3
4
5
6
7
8
9
10
package com.company;
public class Hello {
String name;
public void hello(String name) {
System.out.println(name + "你好,我是" + name);
//输出 “李四你好,我是李四”
System.out.println(name + "你好,我是" + this.name);
//输出 “李四你好,我是张三”
System.out.println("this的地址:"+this);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
运行结果:
这也就是this关键字的原理所在。
# 构造方法
格式:
public 类名称 (参数类型 参数名称) {
方法体
}
2
3
构造方法是专门用来创建对象的方法,当我们通过关键字new来创建对象的时候,其实就是在调用构造方法。
注意事项:
- 构造方法的名称必须和所在的类名称完全一样。
- 构造方法不要写返回值类型,包括void。
- 构造方法不能return一个返回值。
- 如果没有编写任何构造方法,那么编译器会自动写一个构造方法,然后什么事情都不做。
- 构造方法也是可以重载的。
package com.company;
public class Main {
public static void main(String[] args) {
Hello person = new Hello();
}
}
2
3
4
5
6
7
8
package com.company;
public class Hello {
public Hello() {
System.out.println("这是一个构造方法");
}
}
2
3
4
5
6
7
8
运行结果:
构造方法常常用来初始化类中的成员变量:
package com.company;
public class Main {
public static void main(String[] args) {
Student stu = new Student("f4de", 20);
System.out.println("ID:"+stu.getName()+",年龄:"+stu.getAge());
}
}
2
3
4
5
6
7
8
9
10
package com.company;
public class Student {
private String name;
private int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
运行结果:
# 如何定义一个标准的类
- 所有的成员变量全部使用private关键字修饰。
- 为每一个成员变量都编写一对Setter/Getter方法。
- 编写一个无参数的构造方法。
- 编写一个全参数的构造方法。
这样的标准类也被叫做Java Bean(加哇豆)。
下面代码定义了一个标准的学生类:
public class Student {
private String name;
private int age;
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# API
# 什么是API
API的官方解释是一种应用程序接口。Java API是一本字典,是JDK中提供给我们使用的类的说明文档。这些类将底层的代码实现封装了起来,我们不需要关心这些类是如何实现的,只需要学习这些类是如何使用的即可。
# API使用步骤
- 打开帮助文档,看到右上角的输入框。
- 在输入框中输入要查找的类,回车。
- 看包名称,java.lang下的类不需要导包,其他需要。
- 看类的解释和说明 。
- 学习该类的构造方法。
- 学习该类的成员方法。
API在线文档:https://www.apiref.com/java11-zh/index.html
# Scanner类
简单来说,Scanner类可以实现键盘输入数据到程序当中。
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in); //System.in代表从键盘输入
//获取键盘输入的一个数字
int number = sc.nextInt();
//获取键盘输入的一个字符串(字符串中遇到空格会自动截断)
String str = sc.next();
//遇到空格不会截断
System.out.println("键盘输入的数字是:"+number);
System.out.println("键盘输入的字符串是:"+str);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
运行结果:
# 练习1:输入两个整数求和
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner sca = new Scanner(System.in);
System.out.println("数字1:");
int num1 = sca.nextInt();
System.out.println("数字2:");
int num2 = sca.nextInt();
int sum = num1 + num2;
System.out.println("和为:"+sum);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
# 练习2:键盘输入三个数字求最大值
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
int[] array = new int[3];
Scanner sca = new Scanner(System.in);
System.out.println("请输入三个数字:");
for (int i = 0; i < array.length; i++) {
array[i] = sca.nextInt();
}
int max = array[0];
for (int i = 0; i < array.length; i++) {
if (array[i] > max) {
max = array[i];
}
}
System.out.println("最大值为:"+max);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 匿名对象
匿名对象就是只有右边的对象,没有左边的名字和赋值语句。
格式:new 类名称();
匿名对象只能使用唯一一次,下次使用不得不再创建一个新对象。
public class Main {
public static void main(String[] args) {
new Student().name = "F4de";
System.out.println(new Student().getName());
}
}
2
3
4
5
6
public class Student {
public String name;
public String getName() {
return name;
}
}
2
3
4
5
6
7
运行结果:
运行结果之所以是null的原因就是第一次创建匿名对象和第二次创建匿名对象在堆中的地址值其实是不相同的,两个是独立的两个对象,我们对第一次创建的匿名对象中的name进行了赋值,但是打印的时候却没有对另外一个匿名对象的name进行赋值,所以该对象的name为默认值"null"。这也印证了匿名对象只能使用唯一一次的说法。
我们可以利用匿名对象的方式来读取键盘的输入:
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
int num = new Scanner(System.in).nextInt();
System.out.println("输入的数字是:"+num);
}
}
2
3
4
5
6
7
8
9
运行结果:
# Random类
Random类用来生成随机数字。
- 导包:
import java.util.Random;
- 创建:
Random r = new Random();
- 使用:各种使用方法请参考API文档。
获取一个随机的int数字(不确定范围):
import java.util.Random;
public class Main {
public static void main(String[] args) {
int numR = new Random().nextInt();
System.out.println(numR);
}
}
2
3
4
5
6
7
8
生成[0,n)范围的随机int数字:
import java.util.Random;
public class Main {
public static void main(String[] args) {
//nextInt中的参数代表的区间的右端点
//区间是左闭右开
//nextInt(100)代表了生成随机数的范围是[0,100)
int numR = new Random().nextInt(100);
System.out.println(numR);
}
}
2
3
4
5
6
7
8
9
10
11
生成[1,n]范围的随机int数字:
import java.util.Random;
public class Main {
public static void main(String[] args) {
//随机数整体+1即可
int numR = new Random().nextInt(100) + 1;
System.out.println(numR);
}
}
2
3
4
5
6
7
8
9
猜数游戏:
import java.util.Random;
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
int numR = new Random().nextInt(100) + 1;
System.out.println("猜数游戏!!答案范围1~100");
System.out.println("请输入你猜的数字:");
int ans = new Scanner(System.in).nextInt();
while (ans != numR) {
if (numR > ans) {
System.out.println("再大点 ^_^");
}
if (numR < ans) {
System.out.println("小一点 ^_^");
}
ans = new Scanner(System.in).nextInt();
}
System.out.println("猜对了,答案就是"+ans);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
运行结果:
# ArrayList类
数组的长度不可以发生改变,但是ArrayList集合的长度可以改变。
关于ArrayList类中的构造方法和成员方法具体可以参照之前给出的API文档,这里仅记录几种常用的方法。
对于ArrayList来说,有一个尖括号<E>,它代表泛型,就是装在集合中的所有元素,全都是统一的该类型。
import java.util.ArrayList;
public class Main {
public static void main(String[] args) {
//创建了一个ArrayList集合,集合里面全都是String类型的数据
ArrayList<String> list = new ArrayList<>();
System.out.println(list);
}
}
2
3
4
5
6
7
8
9
运行结果:
这里再输出list的值,与之前所学习的数组不同,打印的并不是地址值。对于ArrayList来说,直接打印得到的不是地址值,而是内容。
向集合当中添加数据,需要用到add方法。
import java.util.ArrayList;
public class Main {
public static void main(String[] args) {
//创建了一个ArrayList集合,集合里面全都是String类型的数据
ArrayList<String> list = new ArrayList<>();
System.out.println(list);
list.add("Hello world");
System.out.println(list);
list.add("你好 世界");
System.out.println(list);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
# ArrayList类中常用的方法
# add*方法
public boolean add(E,e):向集合当中添加元素,参数的类型和泛型一致。
# get*方法
public E get(int index):从集合当中获取元素,参数是索引值,返回值就是对应位置的元素。
# remove*方法
public E remove(int index) :从集合当中删除元素,返回值就是被删掉的元素。
# size*方法
public int size() :获取集合的尺寸长度,返回值是集合中包含的元素个数。
# 数组工具类Arrays
java.util.Arrays是一个与数组相关的工具类,里面提供的大量的静态方法来实现数组常见的操作。
# 常用的方法
public static String toString(Array a):将参数数组变成字符串,其格式为:[元素1, 元素2, 元素3, ...]
public static void sort(Array a):按照从小到大的顺序对数组中元素进行排序。
# 数学工具类Math
java.util.Math是与数学相关的工具类,里面提供了大量的静态方法,完成与数学运算相关的操作。
# 常用的方法
public static double abs(double num):取绝对值。
public static double ceil(double num):向上取整。
public static double floor(double num):向下取整。
public static long round(double num):四舍五入。
另外Math.PI代表近似的圆周率常量(double)。
# 字符串
# 字符串的概述和特点
java.lang.String类代表字符串。API文档中说:Java程序中所有的字符串字面值(如“abc”)都是此类的实例体现。也就是说,程序当中所有双引号字符串,都是String类的对象。
字符串的特点:
- 字符串是常量,创建之后不可改变。
- 正是因为字符串不可改变,所以字符串是可以共享使用的。
- 字符串效果上相当于*char[]字符数组,但是底层原理是byte[]*字节数组。
# 创建字符串的常见3+1种方式
3+1:三种构造方法,一种直接创建
三种构造方法:
- public String():创建一个空白字符串,不含有任何内容。
- public String(char[] array):根据字符数组的内容,来创建对应的字符串。
- public String(byte[] array):根据字节数组的内容,来创建对应的字符串。
public class Main {
public static void main(String[] args) {
//使用空参构造方法构造
String str1 = new String();
System.out.println(str1);
//根据字符数组创建字符串
char[] array = {'A', 'B', 'C', 'D'};
String str2 = new String(array);
System.out.println(str2);
//根据字节数组来创建字符串
byte[] byteArray = {97, 98, 99, 100};
String str3 = new String(byteArray);
System.out.println(str3);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
直接创建:String str = "Hello";
# 字符串的常量池
字符串常量池:程序当中直接写上的双引号字符串,就在字符串常量池中。
对于基本类型来说,== 运算符是进行数值的比较。
对于引用类型来说,== 运算符是进行地址值的比较。
public class Main {
public static void main(String[] args) {
String str1 = "abc";
String str2 = "abc";
char[] charArray = {'a', 'b', 'c'};
String str3 = new String(strArray);
System.out.println(str1 == str2);
System.out.println(str1 == str3);
System.out.println(str2 == str3);
}
}
2
3
4
5
6
7
8
9
10
11
12
使用不同方式创建的字符串在进行地址值比较的时候,尽管字符串相同,但是结果却不同。为了更好的说明原因,下面附上一张图:
注意:
- 对于引用类型,== 进行的是地址值比较。
- 双引号直接写的字符串在常量池中,new的不在池当中。
# 字符串相关方法
这里只记录一些常用的成员方法,关于所有的成员方法,请查阅上方给出的API文档。
# 字符串内容比较
方法1:public boolean equals(Object obj)
public class Main {
public static void main(String[] args) {
String str1 = "abc";
String str2 = "abc";
char[] strArray = {'a', 'b', 'c', 'd'};
String str3 = new String(strArray);
System.out.println(str1.equals(str2));
System.out.println(str1.equals(str3));
}
}
2
3
4
5
6
7
8
9
10
11
方法2:public boolean equalsIgnoreCase(String str) --(忽略大小写,进行内容的比较)
public class Main {
public static void main(String[] args) {
String str1 = "abc";
String str2 = "Abc";
System.out.println(str1.equalsIgnoreCase(str2));
}
}
2
3
4
5
6
7
8
# 字符串获取
public int length():获取字符串中含有的字符个数。
public String concat(String str):将当前字符串和参数字符串拼接成为新的字符串。
public char charAt(int index):获取指定索引位置的单个字符。
public int indexOf(String str):查找参数字符串在本字符串中首次出现的索引位置,如果没有则返回-1。
# 字符串截取
public String subString(int index):从参数位置一直到字符串末尾,返回新字符串。
public String stbString(int begin,int end):从begin开始一直截取到end,返回中间的子字符串(包含左边,不包含右边)。
# 字符串转换
public char[] toCharArray():将当前字符串拆分成为字符数组作为返回值。
public byte[] getBytes():获得当前字符串底层的字节数据。
public String replace(CharSequence oldString, CharSequence newString):将所有出现的oldString替换成newString,并返回替换之后新的字符串。
# 字符串分割
public String[] split(String regex):按照参数的规则,将字符串切分成为若干个部分。
public class Main {
public static void main(String[] args) {
String str1 = "aaa,bbb,ccc";
String[] strArray1 = str1.split(",");
for (int i = 0; i < strArray1.length; i++) {
System.out.println(strArray1[i]);
}
}
}
2
3
4
5
6
7
8
9
注意:split
方法的参数其实是一个正则表达式,如果待分割的字符串中出现了 '.' , '*' 等在正则表达式中有特殊含义的字符,则要对这些特殊字符进行转义。
# 一个练习题
题目要求:获取一个键盘输入的字符串,统计其中各种字符出现的个数。
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
int lowChar = 0;
int upChar = 0;
int number = 0;
int elseChar = 0;
String str = new Scanner(System.in).next();
char[] charArray = str.toCharArray();
for (int i = 0; i < charArray.length; i++) {
char char1 = charArray[i];
if (char1 >= 'a' && char1 <= 'z') {
lowChar++;
} else if (char1 >= 'A' && char1 <= 'Z') {
upChar++;
} else if (char1 >= '0' && char1 <= '9') {
number++;
} else {
elseChar++;
}
}
System.out.println("小写字母有" + lowChar + "个");
System.out.println("大写字母有" + upChar + "个");
System.out.println("数字有" + number + "个");
System.out.println("其他字符有" + elseChar + "个");
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# 继承
继承就是子类继承父类的特征和行为,使得子类对象具有父类的实例域和方法。
生活中的继承(图片来自于菜鸟教程):
# 类的继承格式
- 在Java中通过extends关键字申明一个类是从另外一个类中继承而来的。
class 父类 {
···
}
class 子类 extends 父类 {
···
}
2
3
4
5
6
7
- 父类也可称作基类和超类。
- 子类也可叫派生类。
# 继承中方法的重写(覆盖)
重写:在继承关系中,方法的名称一样,参数列表也一样。
重写和重载的区别:
- 重写:方法名称一样,参数列表也一样。
- 重载:方法名称一样,参数列表不一样。
重写的特点:
- 创建的是子类对象,则优先用子类方法。
重写的注意事项:
- 保证父子类之间的方法名称相同,参数列表也相同。
- 子类方法的返回值必须小于等于父类方法的返回值范围。
- 子类方法的权限必须大于等于父类方法权限修饰符。(public > protected > (default) > private)
# super关键字的三种用法
- 在子类的成员方法中,访问父类的成员变量。
- 在子类的成员方法中,访问父类的成员方法。
- 在子类的构造方法中,访问父类的构造方法。
# this关键字的三种用法
- 在本类的成员方法中,访问本类的成员变量。
- 在本类的成员方法中,访问本类的另一个成员方法。
- 在本类的构造方法中,访问本类的另一个构造方法。
注意:
- super和this两种构造调用不能同时使用。
# 继承的三个特点
Java语言是单继承的,也就是说,一个类的直接父类只能有唯一一个。
Java语言可以多级继承。
Java语言可以多重继承。
# 抽象
# 引言
如果父类当中的方法不确定如何进行,那么这种方法就是一个抽象方法。
比方说,对于正方形,三角形,圆形三种类,他们每种图形都有属于自己的计算面积的方法,但是他们都继承自图形类,现在让要求写一个“计算图形面积”的方法,这样的方法是不确定如何实现的,这样的方法就被叫做抽象方法。

# 格式
- 抽象方法:在方法前加上abstract关键字,然后去掉大括号,直接分号结束。
- 抽象类:抽象方法所在的类必须是抽象类,在class前加上abstract关键字即可。
public abstract class Animal {
//抽象方法,代表吃东西,但是不确定吃什么东西
public abstract void eat();
}
2
3
4
5
6
# 使用
- 不能直接new一个抽象类对象,必须使用一个子类来继承抽象父类。
- 子类必须覆盖重写抽象父类当中所有的抽象方法;子类去掉抽象方法中的abstract关键字,然后补上大括号。
//Animal.java
public abstract class Animal {
//抽象方法,代表吃东西,但是不确定吃什么东西
public abstract void eat();
}
//Dog.java
public class Dog extends Animal {
public void eat() {
System.out.println("狗吃骨头");
}
}
//Main.java
public class Main {
public static void main(String[] args) {
Dog dog = new Dog();
dog.eat();
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
运行结果:
# 注意事项
抽象类不能直接创建对象,如果创建则无法通过编译而报错,只能创建其非抽象子类的对象。
抽象类中可以有构造方法,是供子类创建对象时,初始化父类成员使用的。
抽象类中不一定包含抽象方法,但是有抽象方法的类一定是抽象类。
抽象类的子类必须要覆盖抽象父类中所有的抽象方法,否则会报错,除非该子类也是抽象类。
# 接口
# 接口基本格式
接口就是多个类的公共规范,是一种引用数据类型,最重要的就是其中的抽象方法。
如何定义一个接口的基本格式:
public interface 接口名称 {
//接口内容
}
2
3
接口的抽象方法的定义:
接口使用步骤:
- 接口不能直接使用,必须有一个实现类来实现该接口。
格式:
public class 实现类名称 implements 接口名称 {
//...
}
2
3
接口的实现类必须覆盖重写接口中所有的抽象方法。
创建实现类的对象,进行使用。
//Interface1.java
public interface Interface1 {
//抽象方法
public abstract void method1();
}
//Interface1Impl.java
public class Interface1Impl implements Interface1 {
@Override
public void method1() {
System.out.println("接口测试");
}
}
//Main.java
public class Main {
public static void main(String[] args) {
Interface1Impl inter = new Interface1Impl();
inter.method1();
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
运行结果:
注意事项:
- 如果实现类并没有覆盖重写接口的所有抽象方法,那么这个实现类自己必须是抽象类。
# 接口默认方法
从java 8开始,接口里允许定义默认方法。
格式:
public default 返回值类型 方法名称(参数列表) {
方法体...
}
2
3
备注:接口当中的默认方法,可以解决接口升级的问题。
//Interface1.java
public interface Interface1 {
//抽象方法
public abstract void method1();
//默认方法
public default void defaultMethod1() {
System.out.println("这是默认方法");
}
}
//Interface1Impl.java
public class Interface1Impl implements Interface1 {
@Override
public void method1() {
System.out.println("接口测试");
}
}
//Main.java
public class Main {
public static void main(String[] args) {
Interface1Impl inter = new Interface1Impl();
inter.method1();
//inter实现类中没有该默认方法,会自动向接口追溯
inter.defaultMethod1();
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
注意:
- 接口的默认方法,可以通过接口的实现类对象直接调用。
- 接口的默认方法,也可被接口的实现类进行覆盖重写。
# 接口静态方法
从java 8开始,接口中允许调用静态方法。
格式:
public static 返回值类型 方法名称(参数列表) {
方法体...
}
2
3
注意:
- 不能通过接口实现类的对象来调用接口当中的静态方法。
- 正确用法:通过接口名称直接调用其中的静态方法。
//Interface1.java
public interface Interface1 {
public static void staticMethod() {
System.out.println("这是接口的静态方法");
}
}
//Main.java
public class Main {
public static void main(String[] args) {
Interface1.staticMethod();
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 接口私有方法
问题描述:我们需要抽取一个共有方法,用来解决两个默认方法之间重复代码的问题。但是这个共有方法不应该让实现类使用,应该是私有化的。
解决方案:从java 9开始,接口中运行定义私有方法。
- 普通私有方法:解决多个默认方法之间重复代码问题。
格式:
private 返回值类型 方法名称(参数列表) {
方法体
}
2
3
- 静态私有方法:解决多个静态方法之间重复代码问题。
格式:
private static 返回值类型 方法名称(参数列表) {
方法体
}
2
3
# 接口成员变量
接口当中也可以定义成员变量,但是必须使用public static final三个关键字进行修饰。从效果上看,这其实就是接口的【常量】。
注意:
- 一旦使用final关键字进行修饰,表示不可改变。
- 接口当中的常量必须进行赋值。
- 接口当中常量的名称,使用完全大写的字母,而且用下划线进行分隔(推荐命名规则)。
格式:
public static final 数据类型 常量名称 = 数据值;
//使用方式
接口名称.常量名称
2
3
4
# 接口注意事项
使用接口时需要注意:
接口是没有静态代码块或者构造方法。
一个类的直接父类是唯一的,但是一个类可以同时实现多个接口。
格式:
public class ClassName implments InterfaceA, InterfaceB {
//覆盖重写所有的抽象方法
}
2
3
//InterfaceA
package com.company;
public interface InterfaceA {
public abstract void methodA();
}
//InterfaceB
package com.company;
public interface InterfaceB {
public abstract void methodB();
}
//InterfaceImpl
package com.company;
public class InterfaceImpl implements InterfaceA, InterfaceB {
@Override
public void methodA() {
System.out.println("覆盖重写了A方法");
}
@Override
public void methodB() {
System.out.println("覆盖重写了B方法");
}
}
//Main
package com.company;
public class Main {
public static void main(String[] args) {
InterfaceImpl inter = new InterfaceImpl();
inter.methodA();
inter.methodB();
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
运行结果: