由于图片上传问题,我将文件转化为PDF,这样既可以查看文章图片,也可以下载保存
https://download.youkuaiyun.com/download/peterbearXY/15289857
文章目录
引言
学习一门编程语言不是一件难事,在本书的前言我要郑重强调一下这件事。学习如逆水行舟,不进则退;只要大家坚持,相信最后定会有所收获。本书作为一本入门的工具书,会侧重讲解JAVA的基础知识,旨在引导读者进入JAVA的学习大门。
什么是JAVA
JAVA是一种面向对象的高阶编程语言,在JAVA中有句老话:万物皆对象。对于java的介绍我引用百科的一些解释。(Java具有简单性、面向对象、分布式、健壮性、安全性、平台独立与可移植性、多线程、动态性等特点 [2] 。Java可以编写桌面应用程序、Web应用程序、分布式系统和嵌入式系统应用程序等)
第一个JAVA程序
由于JAVA是一门面向对象的语言,在运行程序时我们需要单独建立一个类对象,在此类对象的main()函数中运行。废话不多说,直接上代码
public class hello_world
{
public static void main(String[] args)
{
//这样我们就创立好了第一个程序的框架
//在main函数里面,我们就可以书写一些需要的指令
}
}
作为编程界的惯例,第一个程序当然是在console里面打印"Hello World"
public class hello_world
{
public static void main(String[] args)
{
System.out.println("hello world");
}
}
System.out.println() 是一条将程序中的数据打印在控制台,也就是console中的指令
在System.out.println后面有一对括号,在这个里面我们要传入想要打印在console中的内容,即函数的参数
因为要将”hello world“转化为计算机能看懂的语言,所以我们需要在"hello world"外加上双引号,将其转化为字符串类型
这样计算机就能读懂我们的语言,从而输出我们的指令。
结果
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pfoSMW1z-1613061611808)(C:\Users\23881\AppData\Roaming\Typora\typora-user-images\image-20210128115153341.png)]
JAVA的数据类型
当我们运行JAVA程序时,我们会进行一系列的数学计算,此时拥有存贮数据的变量就尤为重要。在JAVA中为了拥有存储数据的变量,我们往往需要在内存中开辟一道空间,例如:
//在编辑器中的代码变量
int x =5;
int y =6;
//在内存中存储的变量数据, 变量名称我们可以自定义
JAVA的数据类型可以分为2大类
内置数据类型
Java 的内置数据类型一共有8种,8种中又可以分为4种。
- 整型: byte, short, int, long
- 浮点型: float, double
- 字符型: char
- 布尔型: boolean
整型
整型顾名思义,它是用来存储整数的
虽然byte, short, int和long都属于整型,但它们之间还是各有差异因为各自的取值不同。
数据类型 | 数据范围 | 数据大小(bit /位) | 默认值 |
---|---|---|---|
byte | -128~127 (-2^7 ~(2^7)-1) | 1 byte= 8 bits | 0 |
short | -32768~32767 (-2^15 ~(2^15)-1) | 2 bytes = 16 bits | 0 |
int | -2^31 ~ (2^31)-1 | 4 bytes = 32 bits | 0 |
long | -2^63 ~(2^63)-1 | 8 bytes = 64 bits | 0L |
一般情况下,我们使用int类型来定义整数,因为int的范围足以满足我们日常的需求
byte a =100;
short b = 10000;
int c = 100000;
long d =10000000;
浮点型
与整型恰好相反,浮点型是用来存储小数的。浮点型分为float和double,两者的区别在于精度。
数据类型 | 数据范围 | 数据大小(bit /位) | 默认值 |
---|---|---|---|
float (单精度浮点型) | 1.4·10^-45 ~ 3.4·10^38 | 4 bytes = 32 bits | 0.0f |
double(双精度浮点型) | 4.9·10^-324 ~ 1.7·10^308 | 8 bytes = 64 bits | 0.0d |
虽然说浮点数可以表示小数,但是浮点数不能表示精确的数,例如货币。如果需要表示精确的数,需要借助其他java类例如Big Decimal来实现。例子: 1.40* 165 = 231 但是 计算机给出的计算是230.999999999997
通常浮点数在未声明的前提下都是double类型,
float f1 = 234.5f;
double d1 = 123.4;
字符型
char类型是一个单一的16位Unicode字符, 表示字符时用单引号。char的范围是0~65535,大小为16 bits
数据类型 | 数据范围 | 数据大小(bit /位) | 默认值 |
---|---|---|---|
char(字符) | 0~65535 | 2 bytes = 16 bits | ‘u0000’ |
对char变量赋值既可以使用数字,也可以使用字符,char可以存储任何字符
public class test {
public static void main(String[] args) {
char a =65;
char b ='A';
System.out.println(a);
System.out.println(b);
}
}
结果
布尔型
布尔类型指true和false,boolean数据只表示1位信息,可作为标志flag来记录true或false
数据类型 | 数据范围 | 数据大小(bit /位) | 默认值 |
---|---|---|---|
boolean | true / false | 1 bit | false |
引用数据类型
- 数组
- 类与对象
- 字符串
下面有一张图来表示引用类型数据
这张图上p1,p2,p3,p4作为类对象,当他们被创建时,他们实际上时作为指针指向一些数据
在本节引用类型暂时提一下,在后面的章节中会详细解释这些
基础类型转换
在运行程序时,我们有时需要将不同类型的数据转化为同一类型
类型转换又可分为自动类型转换与强制类型转换
自动类型转换
自动转换条件是转换前数据类型的位数低于转换后数据类型的位数
转化过程中可能会有精度损失
byte , short , char 可自动转化为int
int 可转化为 long
long 可转化为 float
float 可转化为 double
public class test {
public static void main(String[] args) {
char a =65;
int b = a;
System.out.println(a);
System.out.println(b);
}
}
结果
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uWyg1G64-1613061611811)(C:\Users\23881\AppData\Roaming\Typora\typora-user-images\image-20210128175039057.png)]
强制类型转换
如果被转化数据类型位数大于要转化的数据类型的位数,此时需要用到强制类型转换,注意Boolean类型不能被强制转换
强制转换格式
public class test {
public static void main(String[] args) {
char a ='A';
byte b = (byte)a; //强制将char类型转化为byte类型
//其实将这些数据打印出来,也暗含了一层数据转换
System.out.println(a);
System.out.println(b);
}
}
结果
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Sf8WSxcn-1613061611813)(C:\Users\23881\AppData\Roaming\Typora\typora-user-images\image-20210128175841728.png)]
JAVA符号
既然JAVA作为一门计算机编程语言,它一定能像计算器一样为我们做一些运算,所以在本章我们将讲解JAVA的运算符号。
算数运算符
运算优先级和现实生活中一样,括号( ) > * 或 % 或 / > + 或 -
符号 | 含义 | 例子 |
---|---|---|
= | 赋值 | int i =5; 把5赋值给i |
+ | 加号 | int i = (5+6); |
- | 减号 | int j = (6-1); |
* | 乘号 | int k = (7*8); |
/ | 取整 | int a = (7/8); |
/ | 除号 | double c = 7.0 /8; |
% | 取余 | int b = (7%8); |
// | 转义字符 | 写注释用的 |
public class test {
public static void main(String[] args) {
int i = 5+6;
int j = 6-1;
int k = 7*8;
int a = 9 / 8;
double b = 9.0 / 8; //如果想用除法获得小数,必须要用double类型的数据
int c = 9 % 8;
System.out.println("5+6 = "+i);
System.out.println("6-1 = "+j);
System.out.println("7*8 = "+k);
System.out.println("(整除)9/8 = "+a);
System.out.println("(除以)9.0/8 = "+b);
System.out.println("(取余)9%8 = "+c);
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EfkgjWuS-1613061611814)(C:\Users\23881\AppData\Roaming\Typora\typora-user-images\image-20210129202007447.png)]
//优先级测试
public class test {
public static void main(String[] args) {
double a = (2+1)+3.0/5-6*7-(4/3);
System.out.println("(2+1)+3.0/5-6*7-(4/3) = "+a); //注意(4/3) 是取整,结果是1
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ACH71YxL-1613061611816)(C:\Users\23881\AppData\Roaming\Typora\typora-user-images\image-20210129202931197.png)]
关系运算符
在运行程序时,我们需要对一些数据进行判断,此时就需要关系运算符
关系运算符判断后,返回true 或 false
符号 | 含义 | 例子 |
---|---|---|
== | 等于 | (6 == 7) false |
!= | 不等于 | (6 != 7) true |
> | 大于 | (6 > 7) false |
< | 小于 | (6 < 7) true |
<= | 小于或等于 | (6 <= 7) true |
>= | 大于或等于 | (6 >= 7) false |
逻辑运算符
和电路图中的逻辑门类似, 逻辑运算符就是
与, 或,非
A为true
B为false
符号 | 含义 | 例子 |
---|---|---|
&& | 与,and,当左边与右边同时为true时返回true | (A && B) false |
|| | 或, or, 左边或右边任意一个满足即返回false | (A||B) true |
! | 非,与所选的条件相反 | !(A||B) false |
补充
看别人的源码时有时会碰到以下的几种符号
符号 | 含义 | 例子 |
---|---|---|
++ | 加一 | a++; a的值加1 |
– | 减一 | b–; b的值减1 |
+= | 加等于 | a += b; 意思是a= a + b; |
-= | 减等于 | a -= b; 意思是 a = a -b; |
/n | 换行 | “/n” |
/t | 水平制表符 | 从左往右数8个空格 |
JAVA逻辑判断
上文我们提到了逻辑运算符与关系运算符,这些符号会给我们返回boolean 类型的数据 :true 或 false
true / false便是用在逻辑判断中的
if / else
假如大家对其他语言,例如python有所了解, 判断通常会使用if else语句,格式如下
if(条件语句){
//执行的指令
}
else if(条件语句){
//执行的指令
}
//else指的是最终条件,即排除if与else if条件之后
else{
//执行的指令
}
例子
在这个例子里面会用到用户输入指令,Scanner
import java.util.Scanner;
public class test {
public static void main(String[] args) {
Scanner input = new Scanner(System.in); //创建scanner对象,这样就可以从控制台输入
System.out.print("a: ");
int a = input.nextInt(); //nextInt()指输入的值是int类型数据,该方法是通过对象调用,后面会讲
System.out.print("b: ");
int b = input.nextInt();
//比较a与b的大小
if(a>b) {
System.out.println("a大于b");
}
else if(a<b) {
System.out.println("a小于b");
}
else {
System.out.println("a等于b");
}
input.close();
}
}
结果
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-j6heCkcD-1613061611817)(C:\Users\23881\AppData\Roaming\Typora\typora-user-images\image-20210129210114533.png)]
除此之外,还有嵌套式的if else
if(a > b){
if(a > c){
//当a大于b 同时 a大于c时,打印"a is MAX"
System.out.println("a is MAX");
}
else{
System.out.println("b < a <c ");
}
}
switch case
switch case 类似于条件判断,通过switch的判断来选取对应的case,格式如下
switch(condition){
case value:
//action
break;
case value2:
//action
break;
default: //当case中的value没有一个满足condition时,default中的指令才会执行
//action
}
例子
//输出输入数字所对应的英文(1-9)
import java.util.Scanner;
public class switch{
public static void main(String[] args) {
System.out.print("NUM: ");
Scanner num = new Scanner(System.in);
int digit = num.nextInt();
String digitName; //String是字符串类型,属于引用数据类型,在后面有提到
switch (digit) {
case 1:
digitName = "one";
break;
case 2:
digitName = "two";
break;
case 3:
digitName = "three";
break;
case 4:
digitName = "four";
break;
case 5:
digitName = "five";
break;
case 6:
digitName = "six";
break;
case 7:
digitName = "seven";
break;
case 8:
digitName = "eight";
break;
case 9: digitName = "nine"; break; //有时为了节省空间,可以连在一起写
default: digitName = "I cannot distinguish this number"; break;
}
System.out.print(digitName);
num.close();
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MZpyKBrc-1613061611819)(C:\Users\23881\AppData\Roaming\Typora\typora-user-images\image-20210130101525956.png)]
每个case后面都跟有一个break,用于阻止case1中的action执行完毕后再执行case2的action
反例
import java.util.Scanner;
public class switch2{
public static void main(String[] args) {
System.out.print("NUM: ");
Scanner num = new Scanner(System.in);
int digit = num.nextInt();
String digitName;
switch (digit) {
case 1: digitName = "one"; break;
case 2: digitName = "two"; break;
case 3: digitName = "three"; break;
case 4: digitName = "four";
case 5: digitName = "five";
case 6: digitName = "six"; break;
case 7: digitName = "seven"; break;
case 8: digitName = "eight"; break;
case 9: digitName = "nine"; break;
default: digitName = "I cannot distinguish this number"; break;
}
System.out.print(digitName);
num.close();
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hbcY1AyX-1613061611820)(C:\Users\23881\AppData\Roaming\Typora\typora-user-images\image-20210130101720253.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ggRlfTKu-1613061611821)(C:\Users\23881\AppData\Roaming\Typora\typora-user-images\image-20210130101740442.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DW41QtCF-1613061611822)(C:\Users\23881\AppData\Roaming\Typora\typora-user-images\image-20210130101843945.png)]
由于没有在case 4和 case 5后面加上break, 无论输入的是4还是5,最终给予digitName的赋值始终是 “six”;
直白点说,break就是用来阻止程序继续向下运行的指令
JAVA循环
学完逻辑判断与基本的运算,接下来我们要了解一下java中的循环,
java的循环如同其他语言一样,包含
- for循环
- while循环
- do while循环
for循环
结构
for(初始值;循环结束条件;初始值变化){
//action
}
在for循环中,初始值一般是我们自定义的,例子如下
//我们创造一个for循环,这个循环运行5次,每次打印我们定义的变量的值
for(int i=0;i<5;i++){
System.out.println(i);
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UxKmB9Ks-1613061611823)(C:\Users\23881\AppData\Roaming\Typora\typora-user-images\image-20210130103416060.png)]
从输出的结果可以看出,当i的值等于5时,条件返回为false,循环终止
while循环
格式
//判断条件指的是满足这个条件时,程序循环运行
while(判断条件){
//action
}
例子
//与上面for循环的案例类似
int i=0;
while(i <5) {
System.out.println(i);
i++; //前面计算符号补充部分有提及,i++指 i = i + 1
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-e0XuzCEk-1613061611824)(C:\Users\23881\AppData\Roaming\Typora\typora-user-images\image-20210130104404416.png)]
如果对这个不太明白,下面有一幅图可以解释
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-at8b94pM-1613061611825)(C:\Users\23881\AppData\Roaming\Typora\typora-user-images\image-20210130105844321.png)]
总共运行了5次,在第6次时条件判断为false,所以循环停止
break
while循环还可以与之前switch case里面提到的break一起使用,例子如下
int i=0;
while(i < 7) {
//当i 等于 5 时,循环中止
if(i == 5) {
break; //break的作用是中止循环
}
System.out.println(i);
i++;
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SRjU0ohf-1613061611826)(C:\Users\23881\AppData\Roaming\Typora\typora-user-images\image-20210130111831321.png)]
continue
与break对应的是continue,break是中止循环
而continue是跳过这一轮循环将要执行的语句,直接进入下一轮循环,例子如下
public class test {
public static void main(String[] args) {
int i=0;
while(i <7) {
System.out.println(i);
i++;
//当i 等于 5时,跳过输出这一轮的 "--------"
//由于i++放在前面,所以输出i后, i的值+1, 即在第5轮,输出i=4过后跳过输出 "--------"
if(i == 5) {
continue;
}
System.out.println("--------");
}
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pjRHZxY4-1613061611827)(C:\Users\23881\AppData\Roaming\Typora\typora-user-images\image-20210130112521021.png)]
do while循环
格式
do{
//action
}while(判断条件);
do while循环和while循环的区别是:无论条件为何,do while循环都会将action部分执行一遍,例子如下
public class test {
public static void main(String[] args) {
int i=0;
do {
System.out.println(i);
i++;
}while(i<0);
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LvRoK3o2-1613061611828)(C:\Users\23881\AppData\Roaming\Typora\typora-user-images\image-20210130124327971.png)]
判断与循环的应用
前面几章我们学习了java的运算符,逻辑判断与循环,这一章举一些例子,灵活运用前面的知识
求最大公因数
题目:求出48与292的最大公因数与最小公倍数,
程序分析:使用辗转相除法
public class LargestFactor {
public static void main(String[] args) {
int m =48;
int n = 292;
int a = m;
int b = n;
int r = a%b;
while (r!=0) {
a = b;
b = r;
r = a%b;
}
int divisor = b;
System.out.println("The Greatest Common Divisor:"+divisor);
System.out.println("The Lowest Common Multiple:"+(m*n)/divisor);
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-S6scExTR-1613061611829)(C:\Users\23881\AppData\Roaming\Typora\typora-user-images\image-20210130155438457.png)]
求100以内的素数
题目:输出100以内的素数?
程序分析:素数是大于1的正整数,只能除以它本身与1
public class isprime {
public static void main(String[] args) {
for(int i=2; i<=100; i++) {
boolean isPrime = true;
for (int j=2; j<i; j++) {
if((i%j) == 0) {
isPrime = false;
break;
}
}
if(isPrime) {
System.out.println(i+" is prime");
}
}
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wZUo3rXp-1613061611829)(C:\Users\23881\AppData\Roaming\Typora\typora-user-images\image-20210130143041470.png)]
不死神兔
题目:古典问题:有一对兔子,从出生后第3个月起都生一对兔子,小兔子长到第3个月后每个月又生一对兔子,假如兔子都不死,问每个月的兔子对数为多少(20个月内)?
程序分析:兔子的规律为数列1,1,2,3,5,8,13,21…(斐波那契数列)
public class Test{
public static void main(String[] args){
long a=1;
long b=1;
long c=0;
System.out.print(a+"\t"+b);
//for循环用来计数
for(int i=3;i<=20;i++){
//a,b,c交换数据
c =a+b;
a =b;
b =c;
System.out.print(c+"\t");
}
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-URMRdTWP-1613061611830)(C:\Users\23881\AppData\Roaming\Typora\typora-user-images\image-20210130140249510.png)]
水仙花数
题目:打印出1000以内的水仙花数,所谓的“水仙花数”是指一个三位数,其各位数字立方和等于该数本身。例如:152是一个“水仙花数”,因为153 =1的3次方+5的3次方+3的3次方。
程序分析:需要用到for循环,每个数分解出个位,十位,百位
public class flower {
public static void main(String[] args) {
//水仙花数是三位数,从100开始,到1000结束
for(int i=100; i<1000; i++ ) {
//第一种获取每位的方法
int a = i/100; //对i取整,获取百位的数字
int b = (i-a*100)/10; // 减去1的百位上的数字,除以10,获取十位的数字
int c = i-100*a-10*b; //个位就是排除十位与百位
//第二种获取每位的方法
//int a = i/100;
//int b = (i%100)/10; //对i除以100取余,再通过10取整,获得百位
//int c = (i%100)%10; //对i除以100取余,再通过10取余,获得个位
int sum = a*a*a+b*b*b*b+c*c*c;
if (sum==i) {
System.out.println(i+"是水仙花数");
}
}
System.out.println("Finish");
}
}
结果
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XXBkYWV8-1613061611831)(C:\Users\23881\AppData\Roaming\Typora\typora-user-images\image-20210130133328919.png)]
99乘法表
题目:输出9*9口诀
程序分析:嵌套式for循环,注意换行
public class test {
public static void main(String[] args) {
for(int i=1;i<=9;i++) {
for(int j=1;j<=i;j++) {
System.out.print(j+"*"+i+"="+(i*j)+"\t");
}
System.out.println();
}
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eSlfSUmN-1613061611831)(C:\Users\23881\AppData\Roaming\Typora\typora-user-images\image-20210130155506835.png)]
求Pi的值
题目:根据Gregory-Leibniz series, 我们可以计算Pi的值,要求算到第10000个级数
程序分析:Pi = (4/1) - (4/3) + (4/5) - (4/7) + (4/9) - (4/11) + (4/13) … 观察发现规律:分母为奇数,偶数-1,
public class test {
public static void main(String[] args) {
double rst=0.0;
for(int i=1;i<=10000;i++){
//判断是偶数还是奇数
if(i%2==1){
rst += 4.0/(2.0*i-1); //分母为奇数1,3,5,7……
}
else{
rst -= 4.0/(2*i-1);
}
}
System.out.println(rst);
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EU9GcD7q-1613061611832)(C:\Users\23881\AppData\Roaming\Typora\typora-user-images\image-20210130160022440.png)]
JAVA函数
函数,大家在初高中应该有所耳闻,例如f (x), f (x) 这个函数有名字f, 同时它也包含一个参数x。设置一个函数可以被我们连续调用,java或者说大部分主流的编程语言都继承了数学中函数的特点。
在本书的前面几章大家应该已经接触到了函数,System.out的println(""),这个就是一个系统类的函数(方法)
我们可以将想要打印在console(控制台)上的内容作为参数传入此函数(方法),它就将内容呈现出来
格式
java的函数(在类对象中也可以叫做方法)格式
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bTA4Pg2Z-1613061611834)(C:\Users\23881\AppData\Roaming\Typora\typora-user-images\image-20210130182507127.png)]
- 函数返回值类型可以为任意数据类型,int, double, char, string等
- public static 都是修饰符,在后卖会讲到
- return 的值类型一定要与定义函数时一致
在函数或方法中参数还有另外一个名字,形参;而我们传入时的参数叫实参(实际的参数)
作用域
说到函数或方法,那一定要提及作用域,即我们创建的参数能够用在那些范围内
下面我们用一个例子来解释
public class test {
public static void main(String[] args) {
int a = 8;
int b = 9;
int c = add(a,b); //当要调用方法时,直接写该函数的名字与应该传入的参数
System.out.println(a);
System.out.println(b);
System.out.println(c);
}
public static int add(int i, int j) {
int a = i + j;
return a;
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EZnODs2e-1613061611835)(C:\Users\23881\AppData\Roaming\Typora\typora-user-images\image-20210130184251449.png)]
在上面的例子中,main主函数中的a, b, c是实参;而add函数中的i, j, a都是形参
我们可以看出虽然在main函数与add函数中a都是我们的参数,但是两者为什么都不冲突呢?
因为作用域的不同导致了两者的不同。在add函数中定义的参数的作用域仅仅在add函数中,不超过add,而main函数中定义的参数的作用域也仅仅在main中,无法扩散到add函数中。
假如main函数想要与add函数交换参数,就必须通过形参和return。
void关键字
有心的小伙伴也许已经发现了我们的main函数为什么没有返回值,且是返回值类型是void
void的英文意思是空,即什么也没有
同理,用在函数上时,表示该函数接收参数但不需要有返回值,这个函数就像一个方法一样,如下
//写一个比较大小的程序
public class test {
public static void main(String[] args) {
int a = 19;
int b = 79;
int c = 100;
MAX(a, b, c); //此时调用MAX方法,没有返回值
}
public static void MAX(int a, int b, int c) {
System.out.println("NUM: "+a+" "+b+" "+c);
int max = a;
if(a < b) {
max =b;
if(b < c) {
max = c;
}
}
System.out.println("MAX is "+max);
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eQRe1Svh-1613061611836)(C:\Users\23881\AppData\Roaming\Typora\typora-user-images\image-20210130190143049.png)]
函数调用其他函数
我们在一个函数中可以调用别的函数,例子如下
public class test {
public static void main(String[] args) {
int a = 19;
int b = 79;
int c = 100;
MAX(a, b, c);
}
public static void MAX(int a, int b, int c) {
System.out.println("NUM: "+a+" "+b+" "+c);
int max = a;
if(a < b) {
max =b;
if(b < c) {
max = c;
}
}
print(max); //直接输入函数名与参数就可以使用该函数
}
//通过print函数我们把max的值打印出来
public static void print(int max) {
System.out.println("MAX is "+max);
}
}
JAVA数组
上文提到了引用类型数据,在本章我将讲解其中一个引用类型数据:数组
对C语言或其他编程语言有所了解的伙伴应该听说过数组这个定义。数组,顾名思义,是将一系列统一的数据放在一起的组合。比如我们想将 ‘h’,‘e’,‘l’,‘l’,‘o’ 这几个字符放在一起之后再使用,此时我们就需要创建一个 字符类型的数组。
为什么说数组是引用类型的呢?
当我们创立一个数组并赋予其值,并不是在内存中直接开辟含有这些数据的数组,而是将数组的指针指向这些数据。直白点说,数组就像浏览器中的收藏夹,我们需要点击哪个链接(数据)时,就打开这个收藏夹(数组)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9hiEMme8-1613061611837)(C:\Users\23881\AppData\Roaming\Typora\typora-user-images\image-20210129163522377.png)]
以char类型的数组为例,接下来将介绍几种创建数组的方法
创建数组
//声明数组变量
char[] words;
char words[];
//创建数组的几种方法
//1
//每一组括号前要先声明数组中存储数据的类型
words = new char[5]; //在后面的括号中放的数据是我们所定义的数组的大小
//2
words2 = new char[5] //与上一个差不多
//当然声明与创建数组可以放在一起
char[] word = new char[5];
//3
char[] words3 = {'h','e','l'.'l','o',};
当第1种和第2种数组创建完毕后,默认内部的5个元素都为’\u0000’(char 类型的默认值);
当第3种数组创建完毕后,默认的元素是初始化的那些元素
import java.util.Arrays;
public class test {
public static void main(String[] args) {
int[] words = new int[5];
char words2[] = new char[5];
char[] words3 = {'h','e','l','l','o'};
//由于数组无法直接通过System.out.println打印出来,
//我们需要调用Arrays包中的toString()方法
System.out.println("words "+Arrays.toString(words));
System.out.println("words2 "+Arrays.toString(words2));
System.out.println("words3 "+Arrays.toString(words3));
}
}
结果
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ougL3cAd-1613061611839)(C:\Users\23881\AppData\Roaming\Typora\typora-user-images\image-20210129170236619.png)]
访问数组元素
创建数组是为了储存数据,所以我们应该要明白如何访问/调用数组中的元素
我们需要通过数组的下标来访问数组中的元素
在计算机编程语言中,数组下标几乎都是从0开始(数组中第一个元素),因为这样可以方便计算机的指针读取数据
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VPnPsy1p-1613061611840)(C:\Users\23881\AppData\Roaming\Typora\typora-user-images\image-20210129172730705.png)]
所以数组中第一个元素下标是0,第二个元素下标是1,依次类推。注意最后一个元素的下标必须是(数组长度-1),因为元素下标是从0开始的。
public class array2{
public static void main(String[] args) {
double[] values = {2,3,4,5};
double one = values[0]; //注意获取数组中元素,接收的变量类型应与数组一致
double two = values[1];
System.out.println(one);
System.out.println(two);
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6IIEPOJg-1613061611840)(C:\Users\23881\AppData\Roaming\Typora\typora-user-images\image-20210129173312581.png)]
有心的同学应该注意到了,我们存进去的元素是整数类型,为什么最后打印出来是double呢?
因为在存入double类型数组中时,编译器进行了自动类型转换,将整型转化为浮点double类型
遍历数组
为了读取数组中的元素,我们通常使用遍历的方法
通过一组for循环来获取数组中的每个元素
public class array2{
public static void main(String[] args) {
double[] values = {2,3,4,5};
//public int length;是java数组中自带的方法,用于返回该数组的长度
for(int i=0;i<values.length;i++) {
System.out.println(values[i]);
}
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mNk9DjfS-1613061611840)(C:\Users\23881\AppData\Roaming\Typora\typora-user-images\image-20210129174548429.png)]
我们还可以使用for-each(也叫增强for)来遍历数组
for(type element : array){
System.out.println(element);
}
增强for允许我们在不访问下标的情况下读取数组,十分方便
public class array2{
public static void main(String[] args) {
double[] values = {2,3,4,5};
for(double value: values) {
System.out.println(value);
}
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xbhNNukY-1613061611841)(C:\Users\23881\AppData\Roaming\Typora\typora-user-images\image-20210129174548429.png)]
数组与函数
我们可以把数组作为一个返回值或参数放在函数中
将数组作为参数传入函数
public class array2{
//add 将数组作为参数传入函数,返回数组中所有元素的和
public static double add(double[] values) {
double total = 0;
for (double element : values)
{
total = total + element;
}
return total;
}
public static void main(String[] args) {
double[] values = {2,3,4,5};
System.out.println(add(values));
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gzZI0CFL-1613061611842)(C:\Users\23881\AppData\Roaming\Typora\typora-user-images\image-20210129184602683.png)]
将数组作为返回值输出
import java.util.Arrays;
//将输入数组每个元素加1,并返回
public class array2{
public static double[] add(double[] values) {
for(int i=0;i<values.length;i++) {
values[i] += 1;
}
return values;
}
public static void main(String[] args) {
double[] values = {2,3,4,5};
System.out.println(Arrays.toString(add(values)));
}
}
多维数组
我们的现实世界是由多个维度所组成,一条线是一维, 一个平面是二维,我们所在的空间是三维
和现实世界类似,数组也可以创造多个维度
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-26KDN6dx-1613061611843)(C:\Users\23881\AppData\Roaming\Typora\typora-user-images\image-20210129185959447.png)]
创建二维数组或多维数组的方式与一维数组类似
public class test {
public static void main(String[] args) {
int[][] num = new int[2][3]; //创建一个2行3列的二维数组
num[0][0] = 1; //给第一行,第一列的元素赋值为1
num[1][0] = 2; //给第二行,第一列的元素赋值为2
//因为是二维数组,通过两个for循环来遍历打印
for(int i=0;i<num.length;i++) {
//num.length是获取二维数组的行数
for(int j=0;j<num[0].length;j++) {
//num[0].length是获取第一行数组的个数,即num二维数组的列数
System.out.print(num[i][j]+" ");
}
System.out.println();
}
}
}
结果
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DgaM6fUI-1613061611843)(C:\Users\23881\AppData\Roaming\Typora\typora-user-images\image-20210129191554891.png)]
在本章中我们有运用到一个数组的工具类, Arrays,在后面常用类中,我们会有详细的解释。
小案例 Tic-Tac-Toe (井字棋)
import java.util.Scanner;
public class TiTaTu{
public static void main(String[] args) {
//用二维数组模拟棋盘, 0和1代表O和X
int [][] array = {
{0,1,0,1},
{1,0,0,1},
{1,1,1,1},
{0,1,0,1},
};
boolean rst_X = false;
boolean rst_O = false;
int numOfX_S = 0;
int numOfO_S = 0;
int numOfX_RS = 0;
int numOfO_RS = 0;
int k = array.length -1;
for(int n=0;n<array.length;n++) {
int numOfX_H =0;
int numOfO_H =0;
for(int m=0;m<array[n].length;m++) {
if(array[n][m] == 1) {
numOfX_H++;
if(numOfX_H == array.length ) {
rst_X = true;
}
}
else if(array[n][m] == 0){
numOfO_H++;
if(numOfO_H == array.length){
rst_O = true;
}
}
}
if(array[n][n] == 1) {
numOfX_S++;
if(numOfX_S == array.length ) {
rst_X = true;
}
}
else if(array[n][n] == 0) {
numOfO_S++;
if(numOfO_S == array.length ) {
rst_O = true;
}
}
//reverse diagonal line
if(array[n][k] == 1) {
numOfX_RS++;
if(numOfX_RS == array.length ) {
rst_X = true;
}
}
else if(array[n][k] == 0) {
numOfO_RS++;
if(numOfO_RS == array.length ) {
rst_O = true;
}
}
k--;
}
if(rst_X == true) {
System.out.print("X win");
}
else if(rst_O == true) {
System.out.print("O win");
}
else {
System.out.print("Equal");
}
}
}
案例2,打印二维数组初始值
public class Print2DArray{
public static void PrintOut(int[][] array) {
for(int i=0; i<array.length;i++) {
for(int j=0;j<array[i].length;j++) {
System.out.printf("%3d", array[i][j]);
}
System.out.println();
}
}
public static void main(String[] args) {
int[][] a = new int[5][5];
PrintOut(a);
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9gpzHDwx-1613061611845)(C:\Users\23881\AppData\Roaming\Typora\typora-user-images\image-20210131235733815.png)]
JAVA类与对象
不知道大家以前有没有听说过这样一句话“万物皆对象”。这句话出自Think in Java,一本今典的java书籍。在java中,任何东西都可以被描述成一个类,且每个类都能被用来实例化对象;因为周围的世界是由各种各样的对象所构成,所以我们可以利用java来模拟我们周围的世界,创建各种各样的类例如:学生类,老师类,汽车类,飞机类。
对象创建
创建一个对象的前提是有这个对象的类,我们先创建一个Animal类
package Class_Study;
public class animal {
private int age; //private是修饰符,后面会讲
public animal(){
//action 我们可以在这里添加一些指令
}
public animal(int num) {
age = num;
}
public void roar() {
System.out.println("I am yelling.");
}
}
拥有了animal这个类后,我们就可以实例化(创建)一个animal对象
animal Tom = new animal();
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5iOegIrB-1613061611846)(C:\Users\23881\AppData\Roaming\Typora\typora-user-images\image-20210131105309311.png)]
以后要是想对animal对象进行一些操作,直接通过Tom调用animal的内部方法(长得像函数的东西)即可。
构造方法
在类中的函数统一称作“方法”
有人也许注意到了在animal类中为什么还有与animal同名的方法呢?
这些方法叫做构造方法,也称构造函数。
无参构造
public animal(){ //public是修饰符
System.out.println("I am an animal."); //在无参构造方法里面,我们可以写一些指令
}
像这一个构造方法,就是无参构造方法,当我们实例化对象时,可以不需要添加参数
animal Tom = new animal();
假如我们书写的类中没有写构造方法,如下
package Class_Study;
public class animal {
public void roar() {
System.out.println("I am yelling.");
}
}
此时,编译器会自动给我们加上一个无参构造方法,等同于这样
package Class_Study;
public class animal {
public animal(){
}
public void roar() {
System.out.println("I am yelling.");
}
}
有参构造
有参构造方法就是在无参构造的基础上增添了参数,方便我们将外部的参数传递给实例化的对象
我们第一个实例化对象的例子中除了有无参构造方法,还有有参构造方法
public animal(int num) {
age = num; //将外部的参数num赋值给对象内部的age
System.out.println("My age is "+ age); //将age的值输出到控制台上
}
因为是有参构造方法,我们实例化时可以加参数
int age = 3;
animal Tom = new animal(age);
//由于作用域的不同,我们在main函数中定义的age并不与animal对象中的age相冲突
注意
当我们既没有写无参构造也没有写有参构造时,编译器会自动给我们加上一个无参构造方法。
但如果我们自己写了有参构造方法或无参构造方法任意一个以后,编译器就不会自动添加无参构造方法。
对象方法
上面也提到了,在类对象中函数叫做方法method, 除了叫法不同,各种功能与函数一致
package Class_Study;
public class animal {
private int age;
public animal(){
}
public animal(int num) {
age = num;
System.out.println("My age is "+ age);
}
public void roar() {
System.out.println("I am yelling.");
}
}
在animal类中,roar就是对象方法。使用roar方法必须通过animal的实例化的对象来调用
public class animalTest {
public static void main(String[] args) {
animal Tom = new animal(3);
Tom.roar();
}
}
结果
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pGFaF1y5-1613061611847)(C:\Users\23881\AppData\Roaming\Typora\typora-user-images\image-20210131113112130.png)]
因为在实例化过程中使用了有参构造方法,有参构造方法中有一行指令是打印age: "System.out.println("My age is “+ age);” 所以在控制台中出现 My age is 3
我们通过Tom来调用类方法
Tom.roar();
所以程序执行此方法,在控制台中打印 “I am yelling”
this关键字
this关键字可以用于在本类中调用,本类方法或实例变量; 它也可以用于调用本类中其他构造方法;
this可以理解为当前对象
一个一个分析;
- 实例变量
当我们方法中的形参与类对象中的参数名字一样时,使用this关键字可以避免运行时的error,如下
没有使用this 关键字时
public class animal {
private int age;
public animal(){
System.out.println("I am an animal.");
}
public animal(int age) {
age = age;
}
public void printAge() {
System.out.println("My age is "+ age);
}
public void roar() {
System.out.println("I am yelling.");
}
}
public class animalTest {
public static void main(String[] args) {
animal Tom = new animal(3);
Tom.printAge();
}
}
此时的编译器
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EtV3iFye-1613061611848)(C:\Users\23881\AppData\Roaming\Typora\typora-user-images\image-20210131115030599.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uVx1YGRR-1613061611849)(C:\Users\23881\AppData\Roaming\Typora\typora-user-images\image-20210131184425189.png)]
为什么我们输的是3,而打印的却是0 ?因为我们并没有将值真正赋给对象的age
为了避免这种无效赋值的情况,我们需要使用this
this的作用相当于告诉编译器,你要把我传进的参数age赋值给类对象的age,然后调用类对象的age打印输出
public class animal {
private int age;
public animal(){
System.out.println("I am an animal.");
}
public animal(int age) {
this.age = age;
}
public void printAge() {
System.out.println("My age is "+ this.age);
}
public void roar() {
System.out.println("I am yelling.");
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dchxMTLs-1613061611850)(C:\Users\23881\AppData\Roaming\Typora\typora-user-images\image-20210131184639113.png)]
- 本类方法
为了表示方便,在本类中调用本类方法,可以在调用的方法前加上this
public class animal {
private int age;
public animal(){
System.out.println("I am an animal.");
}
public animal(int age) {
this.age = age;
System.out.println("My age is "+ this.age);
}
public void roar() {
System.out.println("I am yelling.");
this.Hungry();
}
public void Hungry() {
System.out.println("I am hungry!!");
}
}
这个this加或不加都没关系,编译器会帮我们自动加上
- 调用本类的其他构造方法
this可以调用构造方法,这样能避免相同初始化代码
this()是用来调用无参构造方法的
没加this()
public class animal {
private int age;
public animal(){
System.out.println("I am an animal.");
}
public animal(int age) {
this.age = age;
}
public void printAge() {
System.out.println("My age is "+ this.age);
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zvACJehR-1613061611851)(C:\Users\23881\AppData\Roaming\Typora\typora-user-images\image-20210131185804560.png)]
加上this();
public class animal {
private int age;
public animal(){
System.out.println("I am an animal.");
}
public animal(int age) {
this(); //必须方法构造方法最前面
this.age = age;
}
public void printAge() {
System.out.println("My age is "+ this.age);
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZwmzIw2j-1613061611852)(C:\Users\23881\AppData\Roaming\Typora\typora-user-images\image-20210131185909356.png)]
可见,加上this()就调用了animal的无参构造方法
注意,this();必须在调用的方法最前面
this(a); 可以调用有参构造方法, a是有参构造需要的参数,例子如下
public class animal {
private int age;
public animal(){
this(3);
System.out.println("I am an animal.");
}
public animal(int age) {
this.age = age;
}
public void printAge() {
System.out.println("My age is "+ this.age);
}
}
//测试类
public class animalTest {
public static void main(String[] args) {
animal Tom = new animal();
Tom.printAge();
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-apcvmb6p-1613061611853)(C:\Users\23881\AppData\Roaming\Typora\typora-user-images\image-20210131190535142.png)]
我们直接调用animal的无参构造,但是age是有初始值的,就是我们在无参构造中this(3);传入的age。
方法重载Overload
方法可以重载,当然之前学习的函数也可以重载;重载后的方法可以接收不同参数
所谓的重载Overload针对同一个类,在这个类里面可以包含名字相同但是接收参数不同的的方法。例子如下。
public class animal {
private int age;
public animal(){
System.out.println("I am an animal.");
}
public animal(int age) {
this();
this.age = age;
}
public void printAge() {
System.out.println("My age is "+ this.age);
}
//对printAge进行Overload
public void printAge(int year) {
this.age += year;
System.out.println("After "+year+" years. My age is "+this.age);
}
}
public class animalTest {
public static void main(String[] args) {
animal Tom = new animal(3);
Tom.printAge();
Tom.printAge(4);
}
}
结果
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9I3kwb1h-1613061611854)(C:\Users\23881\AppData\Roaming\Typora\typora-user-images\image-20210131195018061.png)]
其实方法的重载就像无参构造方法与有参构造方法一样
对象封装
封装,顾名思义将数据封起来,不让外界轻易访问。在java开发时,我们有时不希望外界访问或改变我们设计好的数据,此时需要将这些数据封装起来。
为了封装数据,我们需要一些访问修饰符,例如public, private(这些在前面大家应该已经遇到了,但是不知道是什么)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vVjr7yf7-1613061611855)(C:\Users\23881\AppData\Roaming\Typora\typora-user-images\image-20210131201852288.png)]
一般用访问修饰符时,都用public 或 private ,public定义的数据都可以访问,但是private的只有本类才能访问
封装可以使数据更安全,假如说想要获取或改变数据,可以使用get, set, 例子如下
public int getAge() {
return age;
}
public void setAge(int age) {
//有set的好处就是,假设在给age赋值时传入的时string类型或其他类型,set里面可以加上判断
this.age = age;
}
对象继承
介绍
聊完上面几章,我们进入对象中比较重要的环节,对象继承,这就像儿子与父亲一样,所以被继承的类大家称为父类,继承的类称之为子类
子类对于父类的继承
- 子类继承父类所有public方法与属性, 也可以添加自己独有的方法
- JAVA继承遵循单一继承关系, 即一个子类只能继承一个父类
- 当子类添加自己方法时可以对父类方法进行覆盖, override。 例如父类可以使用a方法,子类继承a方法,可以再写一遍a方法,使a方法实现另一种
//继承语法:
class B extends A{ } //B是子类, A是父类, 子类继承父类
例子
package Class_Study;
//animal是父类
public class animal {
private int age;
public animal(){
System.out.println("I am an animal.");
}
public animal(int age) {
this.age = age;
System.out.println("My age is "+ this.age);
}
public void roar() {
System.out.println("I am yelling.");
}
}
package Class_Study;
public class TestForAnimal {
public static void main(String[] args) {
Dog kit = new Dog();
kit.roar();
}
}
class Dog extends animal{
Dog(){
System.out.println("I am a dog.");
}
//方法覆盖override
public void roar() {
//super.roar();这个super关键字后面再讲
System.out.println("I am hungry. I need meat");
}
}
在实例化子类对象时,我们会先调用父类的默认构造方法,再调用子类的构造方法,
这就是为什么会出现"I am an animal"
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zzGaJQ6b-1613061611856)(C:\Users\23881\AppData\Roaming\Typora\typora-user-images\image-20210131234622162.png)]
在上面这个例子中,dog类是animal类的子类,它继承了父类的方法
Dog kit = new Dog(); //实例化子类对象,调用子类的无参构造方法
在具有继承关系的创建中,系统先创建父类再创建子类。 如果有默认的构造方法, 先调用父类A的默认无参构方法,再调用子类B的所属构造方法。 这就是为什么我们调用dog类的无参构造方法时,先打印“I am an animal.”,再打印“I am a dog.”
//方法覆盖override
public void roar() {
System.out.println("I am hungry. I need meat");
}
在子类中,子类拥有与父类相同的方法,这样可以对父类的方法进行覆盖,(方法的Override必须是方法一样)(即访问修饰符+返回值类型+名称+传入参数类型一样)
多态
世间万物都有属于自己的类,这些类之间又可以进行分类,例如汽车,飞机等可以被分在(继承)交通工具类里面;老师,学生,家长可以被分在(继承)人这个类里面。我们可以用交通工具类或人类来作为汽车或学生宽泛的表示方法。
- 使用父类作为方法形参实现多态, 使方法参数类型更为宽泛
- 使用父类作为方法返回值实现多态, 使方法可以返回不同子类对象
例子: A a = new B(); A是B的父类
使用多态时,只能使用父类的方法或子类覆盖父类的方法, 例如
//例子
package Class_Study;
public class animal {
private int age;
public animal(){
System.out.println("I am an animal.");
}
public animal(int age) {
this.age = age;
System.out.println("My age is "+ this.age);
}
public void roar() {
System.out.println("I am yelling.");
}
}
package Class_Study;
public class TestForAnimal {
public static void main(String[] args) {
animal kit = new Dog();
kit.roar(); // 作为父类的kit只能调用子类覆盖后的方法或自己本身的方法
}
}
class Dog extends animal{ //狗类继承了父类animal类,覆盖了roar()这个方法
Dog(){
System.out.println("I am a dog.");
}
//方法覆盖override
public void roar() {
System.out.println("I am hungry. I need meat");
}
public void run() {
System.out.println("I can use four legs to run!!");
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-R2PomW1Q-1613061611856)(C:\Users\23881\AppData\Roaming\Typora\typora-user-images\image-20210202104457940.png)]
假设我们作为父类,再调用子类的方法,例如这个:用kit animal类调用dog子类的run方法,出现错误
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-I5o8Lhwj-1613061611857)(C:\Users\23881\AppData\Roaming\Typora\typora-user-images\image-20210202104550114.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OCuMepQD-1613061611858)(C:\Users\23881\AppData\Roaming\Typora\typora-user-images\image-20210202104646229.png)]
向上转型
格式
//Animal类是Dog类的父类
Animal a = new Dog()
刚才上面那个例子就使用了向上转型的方法(子类转为父类)。
向下转型
向下转型前提是 原来实例是子类(先向上转型):
Animal a = new Dog()
Dog dog = (Dog) a //利用强转向下转型
多态的好处是有很强的扩展性,例如这样
speak(new Cat());
speak(new Dog());//此时我们将cat类与dog类作为参数传进去
public void speak(Animal a){
a.roar(); //函数会自动向上转型,调用cat与dog类中Override父类Animal的roar方法
}
instanceof关键字
向下转型前,应判断引用中对象的真实类型,以达到准确性
语法, (dog instanceof animal)//返回结果为boolean
if (args instanceof type) {
type new_name = (type) args;
}
package Class_Study;
public class TestForAnimal {
public static void main(String[] args) {
animal animal = new Dog();
if (animal instanceof Dog) { //判断animal的真实类型是否是Dog类,是的话进行向下转型
Dog dog = (Dog) aanimal;
dog.run();
}
}
}
class Dog extends animal{
Dog(){
System.out.println("I am a dog.");
}
//方法重写override
public void roar() {
System.out.println("I am hungry. I need meat");
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xQxxYVnP-1613061611859)(C:\Users\23881\AppData\Roaming\Typora\typora-user-images\image-20210202115137520.png)]
super关键字
作为子类,我们继承了父类的方法,但当我们继承父类后并Override父类方法后,我们就不能再使用父类的方法,为了能再次使用,我们需要使用super关键字
和this()一样,super()必须是第一条语句
可以调用父类的属性和方法
super.upload() // 父类的方法
super.value //父类的属性,注意这个属性必须是public公共属性,私有属性无法继承
例子,调用父类属性
public class Value {
public static void main(String[] args) {
B b =new B();
b.print();
}
}
class A{
int value =10;
}
class B extends A{
int value = 20;
public void print() {
int value =30;
System.out.println(value); //就近原则,选取本方法中的value
System.out.println(this.value); //this调用本类中的value
System.out.println(super.value);//super调用父类中的value
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Rhj3h0qR-1613061611859)(C:\Users\23881\AppData\Roaming\Typora\typora-user-images\image-20210202121542748.png)]
例子,调用父类的方法
package Class_Study;
//父类
public class animal {
private int age;
public animal(){
}
public animal(int age) {
this.age = age;
System.out.println("My age is "+ this.age);
}
public void roar() {
System.out.println("I am yelling.");
}
}
package Class_Study;
//子类
public class TestForAnimal {
public static void main(String[] args) {
Dog dog = new Dog();
dog.roar();
}
}
class Dog extends animal{
Dog(){
System.out.println("I am a dog.");
}
//方法重写override
public void roar() {
super.roar(); //通过super调用父类的roar方法
System.out.println("I am hungry. I need meat");
}
public void run() {
System.out.println("I can use four legs to run!!");
}
}
结果
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ObOJS1cO-1613061611860)(C:\Users\23881\AppData\Roaming\Typora\typora-user-images\image-20210202122338431.png)]
super也可以调用父类构造方法
super() // 调用无参构造方法
super(5, 6) //调用有参构造方法
在继承章节开头我们碰见的那个例子里面,当调用我们调用子类构造方法时,系统会自动调用父类的无参构造方法,即在子类无参构造之前加上super();如下
package Class_Study;
//父类
public class animal {
private int age;
public animal(){
//父类的无参构造
System.out.println("I am an animal.");
}
public animal(int age) {
this.age = age;
System.out.println("My age is "+ this.age);
}
public void roar() {
System.out.println("I am yelling.");
}
}
package Class_Study;
//子类
public class TestForAnimal {
public static void main(String[] args) {
Dog dog = new Dog();
}
}
class Dog extends animal{
Dog(){
super();
System.out.println("I am a dog.");
}
//方法重写override
public void roar() {
super.roar();
System.out.println("I am hungry. I need meat");
}
public void run() {
System.out.println("I can use four legs to run!!");
}
}
加上super()
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0Ggqw58j-1613061611862)(C:\Users\23881\AppData\Roaming\Typora\typora-user-images\image-20210202122732411.png)]
没加super()
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gnFoirY4-1613061611862)(C:\Users\23881\AppData\Roaming\Typora\typora-user-images\image-20210202122802897.png)]
编译器默认调父类无参构造方法(在没加super()的前提下),不管子类调用的是否是无参还是有参构造
当C对象继承B对象,B对象继承A对象时,实例化C对象,先调用A的无参构造,然后调用B的无参构造,最后调用C的无参构造
//子类调用父类的有参构造
Dog(){
super(3); //在Dog子类的无参构造方法中,调用父类的有参构造
System.out.println("I am a dog.");
}
结果
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2ZfTYONx-1613061611862)(C:\Users\23881\AppData\Roaming\Typora\typora-user-images\image-20210202123738106.png)]
对象是引用类型
为什么说对象是引用类型?
我们new一个对象,其实是用一个指针指向这个部分数据集合,例子如下
//创建一个Point类,有x,y值
public class Point {
private int x;
private int y;
public void SetPoint(int x, int y){
this.x =x;
this.y =y;
}
public void show() {
System.out.println("("+this.x+" , "+this.y+")");
}
}
public class TestPoint {
public static void main(String[] args) {
//实例化一个Point类
Point a = new Point();
//将a点的地址传入SetPoint函数
SetPoint(a);
//如果对象不是引用类型,a点的x,y值就不会改变
a.show();
}
public static void SetPoint(Point b) {
b.SetPoint(5, 5);
}
}
结果
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ko0rP80P-1613061611862)(C:\Users\23881\AppData\Roaming\Typora\typora-user-images\image-20210202215944627.png)]
上面例子的box diagram
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HQCsyKc1-1613061611863)(C:\Users\23881\OneDrive - Rose-Hulman Institute of Technology\未命名.jpg)]
包装类
由于基本类型不是对象,在万物皆对象的JAVA中,我们有时需要将这些数据封装成对象,这就是包装类
基本数据 | 包装类 |
---|---|
byte | Byte |
short | Short |
int | Integer |
long | Long |
float | Float |
double | Double |
char | Character |
boolean | Boolean |
基本类型封装的好处
- 方便进行对象操作
- 便于数据与字符串之间的转换
例如:将数字1转为字符串1,再将字符串1转为数字1
public class NumDemo1
{
public static void main(String[] args)
{
int i =1;
//把基本数据int封装成Integer
Integer num = new Integer(i);
//数字转为字符串
String word = num.toString();
//打印转成的类型
System.out.println(word.getClass());
//将字符串转为数字,打印转成的类型
System.out.println(Integer.valueOf(word).getClass());
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ikgqHyJ3-1613061611864)(C:\Users\23881\AppData\Roaming\Typora\typora-user-images\image-20210209194231137.png)]
JAVA字符串
在前面大家已经不止一次接触到了字符串String类型数据,String字符串是一个功能强大的类,它可以将多个字符放在一起,如下
char word = 'h';
String words = "hello world";
字符串与字符有一点区别:字符串内容放在双引号内,而字符则是单引号。
特点
- 字符串是常量,创建后不可改变
- 字符串字面值储存在字符串池中,可以共享
- String str = new String(“Java”)在堆里面创建一个对象
常用方法
public int length() //返回字符串长度
public char charAt(int index) //根据下标获取字符
public boolean contains(String str) //判断当前字符串是否包含传进去的字符str
public int indexOf(String str)//查找str首次出现的下标,存在则返回该下标,不存在则返回-1
public int lastIndexOf(String str)//查找字符串当中最后出现的下标索引
public String trim()//去除字符串前后的空格
public String toUpperCase()//全部转为大写
public boolean endWith(String str)//判断字符串是否是以str结尾
public boolean startWith(String str)//同样,也有以str开头的判断
public String replace(char oldChar, char newChar)//将原来旧的字符串替换为新的字符串
public String concat(String s)//返回连接后的字符串, s是加在后面的字符串
public String substring(int beginIndex, int endIndex)//返回一个分割过后的字符串
public char[] toCharArray()//将字符串转换成数组
Arrays.toString(char[] string)//为了将数组内的字符打印出来,调用Arrays工具类
下面用一串代码来演示如何使用这些方法
package StringStudy;
import java.util.Arrays;//为了把数组中的内容转成字符串并打印出来
public class Demo1 {
public static void main(String[] args) {
String words = "hello";
System.out.println(words);
words = "world";
System.out.println(words);
String str = new String("Java");
String str2 = new String("Java");
System.out.println("str与str2是否在同一地址:"+(str == str2));
System.out.println("str与str2是否内容完全相同"+str.equals(str2));
System.out.println("words在第一个的字符串是:"+words.charAt(0));
System.out.println("将words转化为数组后的 Array:"+Arrays.toString(words.toCharArray()));
System.out.println("将words转化为数组后的Array[0]:"+words.toCharArray()[0]);
System.out.println("判断words中是否含有w:"+words.contains("w"));
System.out.println("判断words中是否含有wd:"+words.contains("wd"));
System.out.println("返回对应字符串rd的下标:"+words.indexOf("rd"));
words = "hello";
System.out.println("返回对应字符串l的下标:"+words.indexOf("l"));
System.out.println("返回对应字符串l最后的下标:"+words.lastIndexOf("l"));
String student = " Peter Bear ";
System.out.println("After deleting SPACE in String----"+student.trim()+"----");
System.out.println("将字母全部改为大写:"+student.toUpperCase());
System.out.println("将原来的字符串中的内容进行替换:"+student.replace("ear", "ear is 18 years old."));
String website = "www.peterbear.com";
System.out.println("返回在原来的字符串后面增添内容的字符出:"+student.concat("'s website: ").concat(website));
}
}
结果
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SPyl6PwK-1613061611865)(C:\Users\23881\AppData\Roaming\Typora\typora-user-images\image-20210202231723332.png)]
JAVA修饰符
再类对象封装中我们提到的private, public属于JAVA的访问修饰符;在本章我们将介绍其他的修饰符:非访问修饰符
static
static中文翻译是静态,所以关于static 的方法和属性叫做静态属性和静态方法
使用静态修饰符后程序会发生哪些变化?
-
静态属性和方法利用类名进行调用(假如说一个类仅含有static成员,那么这个类被称作工具类)
-
静态成员是全类所共享的成员
-
static 关键字用来声明独立于对象的静态变量(静态属性),无论一个类实例化多少对象,它的静态变量只有一份拷贝。 静态变量也被称为类变量。局部变量不能被声明为 static 变量。
//静态属性
static int count=10;
例子如下
package modifier;
//创建一个类,这个类有静态属性与非静态属性,含静态方法与非静态方法
public class StaticStudy {
public int cnt=0; //非静态属性
public static int cnt2=0; //静态属性,静态变量
public void add() {
cnt++;
}
public static void add2() {
cnt2++;
}
public void show() {
//因为静态成员是全类所共享的成员,所以非静态方法可以调用静态成员变量
System.out.println("非静态计数器"+cnt);
System.out.println("静态计数器: "+cnt2);
}
}
package modifier;
public class TestStatic {
public static void main(String[] args) {
StaticStudy num = new StaticStudy();//实例化一个类对象
num.show();//初始值,调用show非静态方法
num.add();
StaticStudy.add2();//通过类名调用静态方法
num.show();//add后的值
System.out.println("类名调用静态属性: "+StaticStudy.cnt2);//类名调用静态属性
System.out.println("----实例化第二个对象"+"----");
StaticStudy num2 = new StaticStudy();//再实例化一个类对象
num2.add();
StaticStudy.add2();
num2.show();
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3pDt1S75-1613061611866)(C:\Users\23881\AppData\Roaming\Typora\typora-user-images\image-20210203133932014.png)]
从上面例子我们不难发现无论一个类实例化多少对象,它的静态变量只有一份拷贝。
静态方法
特点
- 静态方法可以直接访问静态成员
private static int cnt3=0;
public static int cnt2=0; //静态属性,静态变量
public static void add2() { //静态方法给cnt2加一
cnt2++;
}
- 但是静态方法不能直接访问非静态成员
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sc5hPlgF-1613061611867)(C:\Users\23881\AppData\Roaming\Typora\typora-user-images\image-20210203125411520.png)]
- 静态方法不能使用this和super关键字
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ARoVq8mg-1613061611867)(C:\Users\23881\AppData\Roaming\Typora\typora-user-images\image-20210203134731231.png)]
- 静态方法不能重写,只能继承,没有多态
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PGG1TWGV-1613061611868)(C:\Users\23881\AppData\Roaming\Typora\typora-user-images\image-20210203135945647.png)]
静态代码块
static{ }
//整个程序执行过程中,只执行一次
//可以执行一些必要的初始化行为
//与其相反的是非静态代码块
{
}
静态代码块中只能放静态成员
系统初始化过程:静态代码块---->main函数---->非静态代码块---->构造函数
这里请参考下面的链接
静态类
倘若一个类是静态的,它一定是静态内部类//这个在讲到内部类的时候再具体分析
final
final意为最终的,最后的,即不可变的
最终类
final修饰的不能被继承, 例如String, Math,System类,这些类的均无法被继承
当FinalStudy准备继承Point类时,由于Point类是被final修饰,所以无法被继承
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-op8xRSgo-1613061611868)(C:\Users\23881\AppData\Roaming\Typora\typora-user-images\image-20210203144245874.png)]
最终变量
final int num = 10;
final static string address = "school";
只能被赋值一次(常量)
1. 在构造方法中赋值
如果有一个对常量赋值,其他的构造方法都要对其进行赋值
2. 创建常量时直接赋值
我们创建了一个Point类,使用final修饰了原点,当我们想要改变原点数据时,失败
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jvHcb2d3-1613061611870)(C:\Users\23881\AppData\Roaming\Typora\typora-user-images\image-20210203145808713.png)]
对于最终变量有两种初始化方法,
第一种:直接创建时就赋值
final int Origion_x=0;
final int Origion_y=0;
第二种:使用代码块进行赋值
final int Origion_x;
final int Origion_y;
{
Origion_x=0;
Origion_y=0;
}
第三种:在构造方法中
final int Origion_x;
final int Origion_y;
Point(){
Origion_x=0;
Origion_y=0;
}
倘若变量用static + final修饰,为静态常量,初始化要在静态代码块中进行
第一种:直接创建时就赋值
static final int Origion_x=0;
static final int Origion_y=0;
第二种:使用代码块进行赋值
static final int Origion_x;
static final int Origion_y;
static{
Origion_x=0;
Origion_y=0;
}
最终方法
不能被子类覆盖或者重写,只能继承
我们给父类的Print方法加上final修饰后,子类FinalStudy无法Override Print方法,所以只能继承该方法
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zfanjxGD-1613061611870)(C:\Users\23881\AppData\Roaming\Typora\typora-user-images\image-20210203144655295.png)]
例子如下
package modifier;
public class FinalStudy extends Point{
}
class Point{
private int x;
private int y;
public void SetPoint(int x, int y) {
this.x =x;
this.y =y;
}
public final void Print() {
System.out.println("( "+this.x+", "+this.y+" )");
}
}
package modifier;
//测试
public class TestFinal {
public static void main(String[] args) {
FinalStudy point = new FinalStudy();
point.SetPoint(1, 2);
point.Print();
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Jlc1QG5v-1613061611871)(C:\Users\23881\AppData\Roaming\Typora\typora-user-images\image-20210203145143119.png)]
对象常量
final修饰基本类型:值不可变
final 修饰引用类型:地址不可改变
例子如下
class Point{
private int x;
private int y;
static final int Origion_x;
static final int Origion_y;
static{
Origion_x=0;
Origion_y=0;
}
public void SetPoint(int x, int y) {
this.x =x;
this.y =y;
}
public void Print() {
System.out.println("原点: "+"( "+Origion_x+", "+Origion_y+" )");
System.out.println("( "+this.x+", "+this.y+" )");
}
}
package modifier;
import java.util.Arrays;
public class TestFinal {
public static void main(String[] args) {
final int[] nums = new int[] {1,3,5,7};
System.out.println(Arrays.toString(nums));
// nums = new int[] {5,6,7};//此时不能对nums再进行赋值,因为已经nums已经变成了常量
nums[0] = 9; //但是nums内部的数据可以进行更改,因为nums是引用类型
System.out.println(Arrays.toString(nums));
final Point p1 = new Point();
p1.SetPoint(6, 6);
p1.Print();
p1.SetPoint(10, 10);
p1.Print();
}
}
结果
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vMvOHovl-1613061611871)(C:\Users\23881\AppData\Roaming\Typora\typora-user-images\image-20210203154357345.png)]
abstract
abstract中文意思是抽象,抽象即为不具体
抽象类
一个类不能同时被abstract和final修饰
当类对象没有具体的含义,可以将该类改为抽象类
例如Animal类,它本身没有任何意义各种方法比较抽象,但是继承Animal类的子类Dog类和Cat类有意义,所以要把Animal类改为抽象类
作用:
- 可以被子类继承,提供共性的属性和方法
- 可以声明为引用,更自然的使用多态
抽象类当中可以包含抽象方法,也可以包含非抽象方法
//语法:
public abstract class Animal{ }
如果一个类被改为抽象类,该类就不可以被实例化, 即不能new 对象;但是我们能够用这个类来作为引用,就像多态向上转型一样
下面是一个关于抽象类的例子
package modifier;
public class TestAbstract {
public static void main(String[] args) {
Dog dog = new Dog();
dog.speak();
Animal bird = new Bird(); //可以声明为引用,更自然的使用多态
bird.speak();
//dog和bird调用父类的walk方法
dog.walk();
bird.walk();
}
}
abstract class Animal{
public abstract void speak();
public abstract void move();
//抽象类中包含非抽象方法
public void walk() {
System.out.println("I am walking");
}
}
class Dog extends Animal{
@Override
public void speak() {
System.out.println("Wang Wang Wang……");
}
@Override
public void move() {
System.out.println("Dog uses his four legs to run");
}
}
class Bird extends Animal{
@Override
public void speak() {
System.out.println("Yin Yin Yin……");
}
@Override
public void move() {
// TODO Auto-generated method stub
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5xEga0Yh-1613061611872)(C:\Users\23881\AppData\Roaming\Typora\typora-user-images\image-20210203162947766.png)]
抽象方法
一但类当中包含抽象方法,此类必须是抽象类
父类对象的方法本身没有意义,但为了实现多态(向上转型)又不得不保留
Animal dog = new Dog();
dog.eat();
抽象方法只有方法的声明,没有方法实现,此外抽象方法只能在抽象类当中
//格式
public abstract void eat();
子类继承父类,子类必须重写父类的抽象方法,否则子类必须是抽象类
//抽象的felid猫科动物类继承抽象的动物类,其中增添了抽象的猫科动物捕猎方法
abstract class felid extends Animal{
abstract void hunt();
}
抽象类与抽象方法的小案例:
建立一个Vehicle的交通工具抽象类和一个主人类,打印主人回家时开的什么车,是什么品牌
package Polymophic_And_Abstract;
public class Master {
private final String name; //将主人的名字封装起来
public Master(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void GoHome(Vehicle vehicle) {
System.out.println(this.getName()+"回家了");
vehicle.run();
}
}
package Polymophic_And_Abstract;
public class TestForVehicle {
public static void main(String[] args) {
Master jim = new Master("Jim");
Vehicle car = new Car("奔驰");
jim.GoHome(car);
}
}
//创建一个抽象的Vehicle类
abstract class Vehicle {
private final String band;
public Vehicle(String band) {
this.band = band;
}
public String getBand() {
return band;
}
//一个抽象方法
public abstract void run();
}
//Car继承抽象的Vehicle交通工具类
class Car extends Vehicle{
public Car(String band) {
super(band);
}
//implements 实现run()方法
@Override
public void run() {
System.out.println(super.getBand()+"牌的车在路上跑");
}
}
结果
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LD8zFs2k-1613061611872)(C:\Users\23881\AppData\Roaming\Typora\typora-user-images\image-20210203170116415.png)]
JAVA接口
介绍
接口相当于是特殊的抽象类,定义方法,组成部分与抽象类类似
接口没有构造方法,所以不能创建对象但是可以implements,一个类可以实现多个接口,例如
C extends A implements B implements C
接口也可以和父类一样实现多态(向上转型)
仅可调用接口中实现的方法,不能调用实例化对象所独有的方法
接口往往可以代表一种能力,一但类对象实现了该接口,它也具备了这种能力
实现接口时,访问修饰符必须是public
接口与抽象类相同之处:
- 可编译成字节码文件
- 不能创建对象
- 可以作为引用类型 // 当作为引用类型时,仅可调用实现了的接口;
- 具有Object类中定义的方法(Object类是所有类对象的父类)
不同之处:
- 接口所有属性都是用public static final修饰
- 接口所有方法都是用public abstract 修饰
- 接口没有构造方法,动态代码块和静态代码块
类无法使用多继承,但是接口可以实现多继承接口
package Interface;
public interface Ability extends flyable, swimmable{
public abstract void fire();
}
interface flyable{
public abstract void fly();
}
interface swimmable{
public abstract void swim();
}
interface关键字
类对象是class,而接口则是interface
public interface A{
public static final String FIELD = "Value"
public abstract void method();
}
注意
接口只能定义:公开静态常量 和***公开抽象方法***
public static final String FIELD="Value"; //公开静态常量,使用时,直接用接口进行调用
public abstract void method();//公开抽象方法只用写返回值类型和方法名
类一但implement就必须实现覆盖接口中全部抽象方法,此类就是该接口的实现类
补充
标记接口,例如Serializable接口
public interface Serializable{}; //表示实现这个接口的对象可以被序列化和克隆
public interface Cloneable{};
案例
- 给人实现飞的能力
//先创建fly的接口
package Interface;
public interface fly {
public abstract void fly();
}
//然后创建一个人类,来实现fly接口
package Interface;
public class Person implements fly{
String name;
int age;
Person(String name, int age){
this.name= name;
this.age = age;
}
public void eat() {
System.out.println(this.name+" 在吃饭……");
}
public void sleep() {
System.out.println(this.name+" 在睡觉……");
}
//由于继承了接口,我们必须实现接口的方法
@Override
public void fly() {
System.out.println(this.name+" can fly");
}
}
//写一个测试类
package Interface;
public class TestPerson {
public static void main(String[] args) {
Person jim = new Person("Jim",20);
jim.fly();
//利用接口作为引用类型
fly baby = new Person("baby", 30);
baby.fly();
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BKnAsRfa-1613061611874)(C:\Users\23881\AppData\Roaming\Typora\typora-user-images\image-20210203194232960.png)]
- USB案例
USB接口是大家熟知的一种接口,风扇,U盘,都是USB接口,我们要将USB接口插入电脑
//1先创建USB接口
package USB_Interface;
public interface USB {
public abstract void Service();
}
//2实现Fan,U盘的接口
//U盘
public class USB_Flash implements USB {
@Override
public void Service() {
System.out.println("USB-Flash has connected to the computer");
}
}
//风扇
public class Fan implements USB{
@Override
public void Service() {
System.out.println("Fan starts to run");
}
}
//3由于我们要将U盘和风扇插入电脑中,所以要创建一个电脑类
package USB_Interface;
public class Computer {
private USB usb1;
private USB usb2;
public USB getUsb1() {
return usb1;
}
public void setUsb1(USB usb1) {
this.usb1 = usb1;
}
public USB getUsb2() {
return usb2;
}
public void setUsb2(USB usb2) {
this.usb2 = usb2;
}
public void Run() {
System.out.println("Computer starts to work!!");
if(this.getUsb1() != null) {
usb1.Service();
}
if(this.getUsb2() != null) {
usb2.Service();
}
}
}
//4最终的测试类
package USB_Interface;
public class Test_USB {
public static void main(String[] args) {
Computer Windows = new Computer();
Windows.setUsb1(new Fan());
Windows.setUsb2(new USB_Flash());
Windows.Run();
}
}
结果
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3MSfafuL-1613061611874)(C:\Users\23881\AppData\Roaming\Typora\typora-user-images\image-20210203200029587.png)]
JAVA内部类
创建在类内部的类叫做内部类,
class Outer{
//外部类
class Inner{
//内部类
}
}
内部类拥有和普通类一样的属性,可以拥有方法,能够去实现接口,继承
如果外部类与内部类成员属性名字相同,则优先访问内部类成员
如果想要访问同名外部类成员,需要先写上外部类名字,再this.name,或直接Outer.this.name
既然已经有了外部类,那为什么还要内部类呢?
好处
- 内部类可以对外部类全部内容进行访问
- 方便访问外部类的私有属性(内部类可直接访问外部类的私有成员而不破坏封装)
成员内部类
package InnerClass;
import InnerClass.OuterClass.InnerClass;
public class NormalInnerTest {
public static void main(String[] args) {
//1. 第一种实例化内部类的方法
//OuterClass outer = new OuterClass();
//InnerClass inner = outer.new InnerClass();
//2. 第二种实例化内部类的方法,一步到位
InnerClass inner = new OuterClass().new InnerClass();
inner.print();
}
}
class OuterClass{
private String words="Outer Class";
class InnerClass{
public void print() {
System.out.println(OuterClass.this.words);
new OuterClass().print();
System.out.println("Inner Class: Hello World");
}
}
public void print() {
System.out.println(words+": Hello World");
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hkMTS7sx-1613061611875)(C:\Users\23881\AppData\Roaming\Typora\typora-user-images\image-20210204213254151.png)]
成员内部类不能定义静态成员
静态内部类
静态内部类可以不依赖外部类对象实例化对象
只有静态内部类可以用static修饰
//实例化静态内部类
Outer.Inner StaticInnerDemo =new Outer.Inner();
案例
package InnerClass;
class Outer{
//外部类中包含静态成员变量与非静态成员变量
private static String inf = "Hello World";
private String inf2 = "GoodBye World";
//创建静态内部类
public static class Inner{
//静态内部类里面包含静态方法与非静态方法
private static void print() {
System.out.println("\t静态成员变量: "+inf);
//静态内部类无法直接调用非静态变量
// System.out.println(inf2);
//由于外部类成员变量为私有,需要借助外部类来访问
Outer out = new Outer();
System.out.println("\t非静态成员变量: "+out.inf2);
}
//非静态方法
public void print2() {
System.out.println("\t非静态方法调用静态成员变量: "+inf);
}
}
//普通内部类
public class Inner2{
//普通成员内部类无法创建静态方法
public void print() {
//但是可以调用外部类中的静态成员变量
System.out.println("\t静态成员变量: "+inf);
System.out.println("\t非静态成员变量: "+inf2);
}
}
//外部类能够创建非静态方法
public void fun() {
//在外部类的方法中实例化内部类
Inner innerDemo = new Inner();
System.out.println("静态内部类的静态方法");
//直接通过内部类类名访问静态方法
Inner.print();
System.out.println("静态内部类的非静态方法");
//访问内部类非静态方法需要通过实例化的对象来访问
innerDemo.print2();
}
}
public class InnerClassDemo1 {
public static void main(String[] args) {
//实例化静态内部类
Outer.Inner StaticInnerDemo =new Outer.Inner();
//实例化普通内部类
Outer shell = new Outer();
shell.print();
Outer.Inner2 NormalInnerDemo = shell.new Inner2();
System.out.println("静态内部类的非静态方法");
StaticInnerDemo.print2();
System.out.println("普通内部类的方法");
NormalInnerDemo.print();
}
}
结果
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-470Blw7C-1613061611875)(C:\Users\23881\AppData\Roaming\Typora\typora-user-images\image-20210204215906597.png)]
局部内部类
把一个类定义在方法内部,这个类叫做局部内部类,就像定义在方法中的变量叫局部变量一样
局部内部类不能定义访问修饰符,即不能定义为static
但是可以包含静态常量,private final static int count =2000;
public void fun(final String temp){
class Inner{
public void print(){
System.out.println(info);
System.out.println(temp);
}
}
//实例化内部类,调用内部类方法
new Inner().print();
}
匿名内部类
在java中处理内部类之外,还有一种匿名内部类。
匿名内部类就是指没有一个具体名称的类,此概念是在接口和抽象类的应用上发展起来的,
有时这种类只用一次,命名又可惜,所以就使用匿名内部类
以之前写的Ability接口为例
//接口
package InnerClass;
public interface Ability extends flyable{
public abstract void swim();
}
interface flyable{
public abstract void fly();
}
package InnerClass;
import Interface.Ability;
public class AnonymousInnerClass {
public static void main(String[] args) {
Ability SuperMan = new Ability(){
@Override
public void swim() {
// TODO Auto-generated method stub
System.out.println("I can swim");
}
@Override
public void fly() {
// TODO Auto-generated method stub
System.out.println("I can fly");
}
};
SuperMan.swim();
SuperMan.fly();
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fPyxVxD5-1613061611875)(C:\Users\23881\AppData\Roaming\Typora\typora-user-images\image-20210204225913164.png)]
使用匿名内部类时,必须继承一个父类或实现一个接口
JAVA泛型
JAVA泛型英文generics,
本质是参数化类型,就是把类型作为参数传递,泛型对象不能被实例化:T type = new T();
这么说大家估计还是觉得这个定义比较抽象,
举个例子:我们要写一个排序方法,这个方法能够对int类型,String类型,double类型都进行排序,此时要使用泛型方法
泛型可以提高代码的重复性,防止类型转换异常,提高代码的安全性
泛型字母含义
E - Element:指集合中的元素
T - Type:Java类型,种类
K - Key:键
V - Value:值
N - Number:数值类型
格式
<T,...> //T是类型占位符,是一种引用类型
泛型方法
泛型方法可以接收不同类型的参数,根据传进去的参数做适当的处理,格式如下
//<T> 声明此方法为泛型方法, T指出返回值是泛型T
//Class<T> 指明泛型T的具体类型, c用来创建泛型T代表的类对象
public <T> T getObject(Class<T> c){
//创建一个泛型对象
T t = c.newInstance();
return t;
}
例子,有三个不同类型的数组,分别打印出其中的值
package GenericsStudy;
public class GenericDemo1 {
//泛型方法
public static < E > void printArray(E [] inputArray) {
//输出数组元素
for (E element : inputArray) {
System.out.printf("%s ", element);
}
System.out.println();
}
public static void main(String[] args) {
//创建不同类型数组
Integer[] intArray = {1, 2, 3, 4, 5};
Double[] doubleArray = {1.1, 2.2, 3.3, 4.4};
Character[] charArray = {'H', 'E', 'L','L','O'};
System.out.println("整型数组元素为:");
//因为是写在本类里面,所以可以直接调用
//如果是测试类写在本类外面,需要通过类名来调用静态方法,
printArray(intArray);
System.out.println("\n双精度型数组元素:");
printArray(doubleArray);
System.out.println("\n字符串数组元素:");
printArray(charArray);
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bNTFMXgx-1613061611876)(C:\Users\23881\AppData\Roaming\Typora\typora-user-images\image-20210205114935980.png)]
案例2
编写一个add方法,传入不同类型的数字类型,并返回相加的值,相加的值是double类型
package GenericsStudy;
public class GenericsDemo5 {
//extends是为了做泛型界定,即泛型T必须是Number的子类
public static <T extends Number> double add(T num1, T num2) {
double sum=0.0;
sum = num1.doubleValue() + num2.doubleValue();
return sum;
}
public static void main(String[] args) {
System.out.println("double 类型相加:5.5 + 6.7 ="+add(5.5, 6.7));
System.out.println("int 类型相加:5 + 7 ="+add(5, 7));
System.out.println("double 与 int 类型相加:5.5+6 ="+add(5.5, 6));
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-h5bQ1WUk-1613061611876)(C:\Users\23881\AppData\Roaming\Typora\typora-user-images\image-20210205211611985.png)]
泛型类
既然方法可以定义为泛型,类也可以定义成泛型
泛型类中可以包含泛型方法,多个类型参数
package GenericsStudy;
public class GenericDemo3<T> {
private T t;
public void add(T t) {
this.t = t;
}
public T get() {
return this.t;
}
public static void main(String[] args) {
GenericDemo3<Integer> one = new GenericDemo3<Integer>();
GenericDemo3<String> two = new GenericDemo3<String>();
one.add(1);
two.add("Hello World");
System.out.println(one.get());
System.out.println(two.get());
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-I2ZSAv5l-1613061611877)(C:\Users\23881\AppData\Roaming\Typora\typora-user-images\image-20210205214226660.png)]
上面提到了,泛型可以存储多个类型,下面我们来写一个简易字典,仅包含一对key和value
package GenericsStudy;
public class GenericDemo6 {
public static void main(String[] args) {
Dictionary<String, Integer> dic1 = new Dictionary<String, Integer>();
dic1.put("Peter",110);
System.out.println(dic1.getKey());
System.out.println(dic1.getValue());
System.out.println(dic1.getAll());
}
}
class Dictionary<K, V>{
private K key;
private V value;
public void put(K key, V value) {
this.key = key;
this.value = value;
}
//获取key
public K getKey() {
return key;
}
//获取value
public V getValue() {
return value;
}
//获取key与value的字符串
public String getAll() {
return key.toString()+":"+value.toString();
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fJEknvNO-1613061611878)(C:\Users\23881\AppData\Roaming\Typora\typora-user-images\image-20210205215953250.png)]
泛型接口
继承了泛型接口的类会变成泛型类
package GenericsStudy;
public class GenericDemo7 {
public static void main(String[] args) {
ImpleData<String> Imple = new ImpleData<String>();
Imple.print("Hello World");
}
}
//创建一个泛型接口
interface Data<T>{
T getData(T t1);
}
//ImpleData实现了Data接口
class ImpleData<T> implements Data<T>{
@Override
public T getData(T t1) {
return t1;
}
public void print(T t1) {
System.out.println("Print: "+this.getData(t1));
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-S8qvVnlv-1613061611879)(C:\Users\23881\AppData\Roaming\Typora\typora-user-images\image-20210205221855579.png)]
通配符
通配符是指这个 问号:?,
List <?> seq = new ArrayList<>();
- <? extends T>表示该通配符所代表的类型是T类型的子类。
? extends通常与List一起用,List是一种存储数据的容器和Array数组类似,在后面的章节有介绍
package GenericsStudy;
import java.util.List;
public class GenericDemo4 {
public static void main(String[] args) {
/*由于我们extends Number了,种类只能是Number类或Number的子类
如果使用String类型,会报错 */
List<Number> sequence = new ArrayList<>();
sequence.add(1);
sequence.add(2);
sequence.add(1.4);
sequence.add(7.9f);
getDate(sequence);
}
public static void getDate(List<? extends Number> data) {
for(Object i : data) {
System.out.println(i);
}
}
}
- <? super T>表示该通配符所代表的类型是T类型的父类。
-
不能同时声明泛型通配符上界和下界
JAVA集合
我们Java后端一般要进行数据处理,假设所有数据都是储存在数组里面,大小长度受限,不方便;所以我们在本章将引进集合的概念,集合是对象的容器,定义了对多个对象进行操作的常用方法,可实现数组的功能,但比数组更灵活。注意:数组可以存储基本类型,如int,但是集合只能存储引用类型,如String,所以在集合中存数字时,要将int类型转化为Integer类型
对于集合中的元素我们想要遍历有2种方法
- 增强for(for each循环)
for(Object i :container1) {
System.out.println(i);
}
- 通过迭代器来遍历
//迭代器是通过集合内部的方法来获得的
Iterator<Object> itr = container1.iterator();
//itr.hasNext()是判断是否还有下一个元素,如果是,该循环开始执行
while(itr.hasNext()) {
//打印出下一个元素
System.out.println(itr.next());
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5kVeBFEv-1613061611880)(C:\Users\23881\AppData\Roaming\Typora\typora-user-images\image-20210207164038116.png)]
Collection接口
Collection接口是众多集合的父接口,List接口,Set接口都是继承Collection接口,然后通过实现类实现其中的方法。由于Collection,Set, List都是接口,创建时必须要用实现类;比如List的实现类ArrayList,Set的实现类HashSet.
//引入集合模块
import java.util.Collection
//创建集合,由于Collection是接口,我们用Collection的实现类ArrayList来实现
//Collection集合是泛型集合,< >中我们要添加存储的数据类型, Object类是所有类的父类
Collection<Object> container1 = new ArrayList<>();
由于集合是无法储存基本类型的,当我们存入基本类型时,集合会自动装箱
list.add(20); 实际上是list.add(new Integer(20));
如果有一些方法解释不全,可以参考以下API链接
API: https://www.runoob.com/manual/jdk1.6/
常用方法 | 作用 | 例子 |
---|---|---|
boolean add(E e) | 添加元素,添加成功返回true | container1.add(“apple”); |
void clear() | 移除collection中的所有元素 | container1.clear(); |
int size() | 返回集合中元素的个数 | int size = container1.size(); |
boolean contains(Object o) | 如果此 collection 包含指定的元素,则返回 true | container1.contains(“apple”); |
Iterator iterator() | 返回collection的所有元素的迭代器 | Iterator itr = container1.iterator(); |
boolean isEmpty() | 判断此集合是否为空 | container1.isEmpty(); |
boolean remove(Object o) | 移除集合中的某对象 | container1.remove(1); |
Object[] toArray | 将此集合转为数组 | Object[ ] arrray = container1.toArray(); |
例子1
package CollectionStudy;
import java.util.ArrayList;//引入ArrayList
import java.util.Collection;//引入Collection
import java.util.Iterator; //引入迭代器
public class CollectionDemo1 {
public static void main(String[] args) {
//使用多态
Collection<Object> container1 = new ArrayList<>();
//添加元素
container1.add("apple");
container1.add(1);
//增强for遍历元素
System.out.println("----增强For----");
for(Object i :container1) {
System.out.println(i);
}
//迭代器遍历元素
System.out.println("----Iterator----");
Iterator<Object> itr = container1.iterator();
while(itr.hasNext()) {
System.out.println(itr.next());
}
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0fxJJdV0-1613061611880)(C:\Users\23881\AppData\Roaming\Typora\typora-user-images\image-20210207115019530.png)]
例子2
Collection集合可以存储其他引用数据类型,我们创建一个学生类,并将学生类添加进ArrayList集合(Collection的实现类)
package CollectionStudy;
//Students类
public class Students {
private int age;
private String name;
public Students(int age, String name) {
super();
this.age = age;
this.name = name;
}
public int getAge() {
return age;
}
public String getName() {
return name;
}
//重写toString方法,按照我们的规定返回字符串
@Override
public String toString() {
// TODO Auto-generated method stub
return "Student [ name: "+this.getName()+", age: "+this.getAge()+" ]";
}
}
//测试类
package CollectionStudy;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
public class CollectionDemo2 {
public static void main(String[] args) {
Students Tom = new Students(18, "Tom");
Students Jimmy = new Students(22, "Jimmy"); //先实例化2个students对象
Collection<Students> container = new ArrayList<Students>();
container.add(Tom);
container.add(Jimmy);
//遍历 1
for(Object student : container) {
System.out.println(student);
}
System.out.println("\t----Line-----");
//遍历 2
Iterator<Students> itr = container.iterator();
while(itr.hasNext()) {
System.out.println(itr.next());
}
}
}
结果
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5vVj96uo-1613061611881)(C:\Users\23881\AppData\Roaming\Typora\typora-user-images\image-20210207165739022.png)]
我们想用集合的contains方法,来判断是否包含某引用元素
Students Jimmy = new Students(22, "Jimmy");
Collection<Students> container = new ArrayList<Students>();
container.add(Jimmy);
System.out.println(container.contains(Jimmy));
结果是:true
假设我们传进去的不是Jimmy,而是: new Students(22, “Jimmy”)
结果是:false
为了避免这个问题,我们需要重写students类中的equals方法
//在Students类中重写此方法
@Override
public boolean equals(Object obj) {
if(this == obj) {
return true;
}
if(obj == null) {
return false;
}
if(obj instanceof Students) {
Students students = (Students) obj;
if(this.name.equals(students.name)&&this.age == students.age) {
return true;
}
}
return false;
}
再次调用测试类
结果为:true
List接口
List是有序(添加顺序与遍历顺序一致),有下标,元素可重复的集合
List的独有方法
如果有一些方法解释不全,可以参考以下API链接
API: https://www.runoob.com/manual/jdk1.6/
方法 | 解释 | 例子 |
---|---|---|
void add(int index, Object o) | 在index位置插入对象o | conatiner2.add(0, “Hello”); |
boolean addAll(int index, Collection c) | 将一个集合元素增添到index位置 | container2.addAll(0,container1); |
Object get(int index) | 返回集合中指定位置的元素 | container2.get(0); |
List subList(int fromIndex, int toIndex) | 返回fromIndex与toIndex之间的元素 | container2.subList(0, 5); |
ArrayList
ArrayList是由数组结构实现的,查询快,增删慢
增添包:import java.util.ArrayList;
构造方法:ArrayList container = new ArrayList<>();
E为泛型即ArrayList可以增添任何类
常用方法 | 解释 |
---|---|
public boolean add(E e); | 添加元素 |
public E get(int index) | 按照索引获取元素,从0开始 |
public E set(int index, E element) | 可以替换指定元素 |
public boolean remove(Object 0) | 根据内容删除元素 |
public E remove(int index) | 根据index删除元素,返回删除元素 |
例子
我们要创建一个Students类,将几个students对象存入ArrayList类中
package CollectionStudy;
public class Students {
private int age;
private String name;
public Students(int age, String name) {
super();
this.age = age;
this.name = name;
}
public int getAge() {
return age;
}
public String getName() {
return name;
}
@Override
public String toString() {
// TODO Auto-generated method stub
return "Student [ name: "+this.getName()+", age: "+this.getAge()+" ]";
}
}
package CollectionStudy;
import java.util.ArrayList;
import java.util.Arrays;
public class ArrayListCollection {
public static void main(String[] args) {
ArrayList<Students> container = new ArrayList<>();
Students Jim = new Students(18, "Jim");
Students Tom = new Students(20, "Tom");
//成功add元素后,返回是否成功
System.out.println("是否增添了Jim:"+container.add(Jim));
container.add(Tom);
System.out.println("获取第一个元素:"+container.get(0));
//把第2个元素设置为Alex
container.set(1, new Students(78, "Alex"));
System.out.println("第2个元素是:"+container.get(1));
//container包含的元素个数
System.out.println("元素个数:"+container.size());
//将ArrayList转化为数组
Object[] array = container.toArray();
System.out.println("将ArrayList转化为数组:"+Arrays.toString(array));
//删除元素,删除Jim的信息
System.out.println("删除: "+container.remove(Jim));
System.out.println("删除后个数:"+container.size());
}
}
结果
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-f4FSI5pI-1613061611882)(C:\Users\23881\AppData\Roaming\Typora\typora-user-images\image-20210208192224042.png)]
假如我们增添,删除时用的不是Jim, 而是new Students(18, “Jim”); ?
package CollectionStudy;
import java.util.ArrayList;
import java.util.Arrays;
public class ArrayListCollection {
public static void main(String[] args) {
ArrayList<Students> container = new ArrayList<>();
Students Jim = new Students(18, "Jim");
// Students Tom = new Students(20, "Tom");
System.out.println("是否增添了Jim:"+container.add(Jim));
container.add(new Students(20, "Tom"));
System.out.println("获取第一个元素:"+container.get(0));
//把第2个元素设置为Alex
container.set(1, new Students(78, "Alex"));
System.out.println("第2个元素是:"+container.get(1));
//container包含的元素个数
System.out.println("元素个数:"+container.size());
//将ArrayList转化为数组
Object[] array = container.toArray();
System.out.println("将ArrayList转化为数组:"+Arrays.toString(array));
//删除元素,注意,此时我们new Students(18,"Jim");
System.out.println("删除: "+container.remove(new Students(18, "Jim")));
System.out.println("删除后个数:"+container.size());
}
}
结果
即使我们删除new Students(18,“Jim”);(内容相同),但是还是无法完全删除,删除返回为false
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Aiks5UEV-1613061611882)(C:\Users\23881\AppData\Roaming\Typora\typora-user-images\image-20210208192723418.png)]
所以此时我们需要重写Students里面的equals方法(ArrayList判断remove时调用的方法)
@Override
public boolean equals(Object obj) {
if(this == obj) {
return true;
}
if(obj == null) {
return false;
}
if(obj instanceof Students) {
Students students = (Students) obj;
if(this.name.equals(students.name)&&this.age == students.age) {
return true;
}
}
return false;
}
结果,成功删除
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CODsJJaJ-1613061611883)(C:\Users\23881\AppData\Roaming\Typora\typora-user-images\image-20210208193019582.png)]
LinkedList
Link意思为链,链表集合就像所以数据被链子连接在一起一样
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-T7g0GKYZ-1613061611884)(C:\Users\23881\AppData\Roaming\Typora\typora-user-images\image-20210208193746827.png)]
特点:链表结构实现,增删快,查询慢
使用方法与ArrayList类似
例子
继续用LinkedList存储Students类的信息
同样,我们先不重写equals方法
package CollectionStudy;
public class Students {
private int age;
private String name;
public Students(int age, String name) {
super();
this.age = age;
this.name = name;
}
public int getAge() {
return age;
}
public String getName() {
return name;
}
@Override
public String toString() {
// TODO Auto-generated method stub
return "Student [ name: "+this.getName()+", age: "+this.getAge()+" ]";
}
}
package CollectionStudy;
import java.util.LinkedList;
public class LinkedListDemo1 {
public static void main(String[] args) {
LinkedList<Students> container = new LinkedList<>();
System.out.println("增添Tom: "+container.add(new Students(20, "Tom")));
container.add(new Students(18, "Jim"));
//pop从container中弹出元素
System.out.println("Pop第一个元素:"+container.pop());
container.add(new Students(18, "Jim"));
container.add(new Students(72,"Trump"));
System.out.println("删除元素Jim:"+container.remove(new Students(18, "Jim")));
//遍历
for(Object i :container) {
System.out.println(i);
}
}
}
结果Jim无法被删除,同时***我们还发现List可以存储相同的元素***
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vsjNqaj3-1613061611885)(C:\Users\23881\AppData\Roaming\Typora\typora-user-images\image-20210208200323333.png)]
此时,我们再重写Students的equals方法
@Override
public boolean equals(Object obj) {
if(this == obj) {
return true;
}
if(obj == null) {
return false;
}
if(obj instanceof Students) {
Students students = (Students) obj;
if(this.name.equals(students.name)&&this.age == students.age) {
return true;
}
}
return false;
}
结果,成功删除
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dNaB7hId-1613061611885)(C:\Users\23881\AppData\Roaming\Typora\typora-user-images\image-20210208200612292.png)]
Set接口
上文我们介绍了List接口与其的实现类,现在我们开始讲Set接口
Set是无序(添加顺序与遍历顺序不一致),无下标,***元素不可重复***的集合
但是使用new可以插入内容相同的元素,因为Set判断的依据是equals方法,如果equals方法未重写则根据地址判断.
例子引入
package setStudy;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
public class SetIntroduction {
public static void main(String[] args) {
//我们使用Set集合的实现类HashSet来创建集合
Set<Object> container = new HashSet<>();
container.add("Hello");
container.add("World");
container.add("Hello");
container.add("My Friend");
Iterator<Object> itr = container.iterator();
while(itr.hasNext()) {
System.out.println(itr.next());
}
}
}
这里由于是字符串,系统内部有equals方法,所以第二个"Hello"无法加入Set集合
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rxbghmdD-1613061611886)(C:\Users\23881\AppData\Roaming\Typora\typora-user-images\image-20210208202251947.png)]
HashSet
HashSet是Set集合的一个实现类
存储结构:哈希表(数组+链表+红黑树)
存储过程
(1)根据hashcode计算保存的位置,如果此位置为空,则直接保存,如果不为空,则执行第二步
(2) 再执行equals方法,如果equals方法为true,则认为重复,否则形成链表,即添加到HashSet容器中
因为HashSet是以equal与hashcode进行比较,所以要重写hashcode与equals,这样就可以排除new的干扰。
构造方法
//E为任意种类的数据
HashSet<E> container = new HashSet<>();
基本操作与List / Set / Collection类似
增添元素:add(E e);
删除元素:remove(E e);
也有迭代器:Iterator iterator();
package setStudy;
import java.util.HashSet;
public class HashSetStudy {
public static void main(String[] args) {
HashSet<String> container = new HashSet<>();
container.add("tim");
container.add("Ketty");
container.add("Yellow");
System.out.println("HashSet中的内容:"+container.toString());
System.out.println("HashSet的大小:"+container.size());
System.out.println("此HashSet中是否含有Ketty数据:"+container.contains("Ketty"));
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YvBItWvM-1613061611887)(C:\Users\23881\AppData\Roaming\Typora\typora-user-images\image-20210208203127750.png)]
案例2
像ArrayList一样,我们创建Students类,增添Students元素
package setStudy;
public class Students {
private int age;
private String name;
public Students(int age, String name) {
super();
this.age = age;
this.name = name;
}
public int getAge() {
return age;
}
public String getName() {
return name;
}
@Override
public String toString() {
return "Student [ name: "+this.getName()+", age: "+this.getAge()+" ]";
}
}
package setStudy;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
public class HashSetDemo2 {
public static void main(String[] args) {
HashSet<Object> container = new HashSet<>();
System.out.println("添加成功:"+container.add(new Students(18, "Jim")));
System.out.println("添加成功:"+container.add(new Students(19, "Jone")));
System.out.println("添加成功:"+container.add(new Students(18, "Alex")));
System.out.println("添加成功:"+container.add(new Students(18, "Jim")));
Iterator<Object> itr = container.iterator();
while(itr.hasNext()) {
System.out.println(itr.next());
}
}
}
结果
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-INqkJjIu-1613061611888)(C:\Users\23881\AppData\Roaming\Typora\typora-user-images\image-20210208204141673.png)]
当我们增添内容相同的元素new Students(18, “Jim”)时,系统先判断是否是同一地址,如果没有看是否重写了equals和hashcode方法。因为判断地址不一样又没重写equals方法(照系统默认的equals方法),所以添加成功
现在我们重写Students类的equals和hashcode方法
@Override
public boolean equals(Object obj) {
if(this == obj) {
return true;
}
if(obj == null) {
return false;
}
if(obj instanceof Students) {
Students students = (Students) obj;
if(this.name.equals(students.name)&&this.age == students.age) {
return true;
}
}
return false;
}
@Override
public int hashCode() {
//第一种转换string的方法
// String age = String.valueOf(this.age);
//第二种
// String age = this.age+"";
String age = Integer.toString(this.age);
return age.hashCode()*this.name.hashCode();
}
结果,相同内容的数据添加失败
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GX7opgtZ-1613061611888)(C:\Users\23881\AppData\Roaming\Typora\typora-user-images\image-20210208204223172.png)]
TreeSet
实现Set接口的还有一个类,TreeSet,基于排列顺序实现元素不重复
此实现为基本操作(add
、remove
和 contains
)
实现了SortedSet接口,对集合元素自动排序
传入TreeSet的元素对象类型必须实现Comparable接口(指定排序规则)
因为TreeSet是通过ComparaTo方法确认是否为重复元素,所以可以通过内部类来override比较方法
使用TreeSet时,最好不要用Object类
package setStudy;
import java.util.TreeSet;
public class TreeSetStudy {
public static void main(String[] args) {
TreeSet<String> container = new TreeSet<>();
container.add("Hello");
container.add("xyz");
container.add("world");
container.add("hello");
container.add("99");
System.out.println(container.toString());
}
}
String类型,(0,1,2…)默认数字在前–>大写–>小写(a,b,c…z)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qX8419Ic-1613061611889)(C:\Users\23881\AppData\Roaming\Typora\typora-user-images\image-20210208204509037.png)]
案例2
还是Students的案例
package setStudy;
public class Students {
private int age;
private String name;
public Students(int age, String name) {
super();
this.age = age;
this.name = name;
}
public int getAge() {
return age;
}
public String getName() {
return name;
}
@Override
public String toString() {
return "Student [ name: "+this.getName()+", age: "+this.getAge()+" ]";
}
}
package setStudy;
import java.util.Iterator;
import java.util.TreeSet;
public class TreeSetStudy {
public static void main(String[] args) {
TreeSet<Students> container = new TreeSet<>();
System.out.println("添加成功:"+container.add(new Students(18, "Jim")));
System.out.println("添加成功:"+container.add(new Students(19, "Jone")));
System.out.println("添加成功:"+container.add(new Students(18, "Alex")));
System.out.println("添加成功:"+container.add(new Students(18, "Jim")));
//遍历
Iterator<Students> itr = container.iterator();
while(itr.hasNext()) {
System.out.println(itr.next());
}
}
}
由于我们再Students类中没有重写CompareTo方法,报错
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NIbDItbe-1613061611890)(C:\Users\23881\AppData\Roaming\Typora\typora-user-images\image-20210208205925362.png)]
继承Comparable接口重写CompareTo方法
package setStudy;
public class Students implements Comparable<Object>{
private int age;
private String name;
public Students(int age, String name) {
super();
this.age = age;
this.name = name;
}
public int getAge() {
return age;
}
public String getName() {
return name;
}
@Override
public String toString() {
// TODO Auto-generated method stub
return "Student [ name: "+this.getName()+", age: "+this.getAge()+" ]";
}
@Override
public int compareTo(Object obj) {
Students stu = (Students) obj;
return (stu.name.hashCode() - this.name.hashCode());
}
}
此时TreeSet根据我们重写的compareTo方法来判断Object是否重复,我们的判断依据是students的名字的hashcode。
结果
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9iik1hVF-1613061611890)(C:\Users\23881\AppData\Roaming\Typora\typora-user-images\image-20210208211200435.png)]
Map接口
Map是一种将Key与value(键对象与值对象)相互映射的集合
Key | Value |
---|---|
1 | Biden |
2 | Trump |
有时能用Map就不要用List,查找信息Map使用效率有时比List要高
//通过HashMap这个实现类来创建一个Map对象
//Students是传入Key的类型, String是value的类型
Map<Students, String> container = new HashMap<>();
常用方法 | 解释 |
---|---|
V put(K key, V value) | 放入键值对 |
Object get(Object Key) | 通过key来获取值value |
Set KeySet() | 返回所有的Key的Set集合 |
Collection values() | 返回包含所有值的collection集合 |
boolean containsKey(Object key) | 如果此映射包含对于指定键的映射关系,则返回 true |
boolean containsValue(Object Value) | 如果此映射将一个或多个键映射到指定值,则返回 true |
V remove(Object key) | 如果存在,根据key移除此键值对 |
如果有一些方法解释不全,可以参考以下API链接
API: https://www.runoob.com/manual/jdk1.6/
实现Map接口的类有HashMap, TreeMap等
Map遍历
- KeySet()
Map <String, String> map = new HashMap<>();
Set<String> key = map.KeySet(); //是所有的Key的集合. 随后可以使用增强for 或迭代器去遍历
System.out.println("\tKeySet");
for( Students i : container.keySet() )
{
System.out.println(i.getName()+"----"+container.get(i));
}
- EntrySet()
EntrySet()的效率要略高于KeySet()
for(Entry<Students, String> i :container.entrySet())
{
System.out.println(i.getKey().getName()+"----"+i.getValue());
}
HashMap
HashMap和HashSet类似,也是存储数据的集合;HashSet是存储值数据,而HashMap是存储键值对数据
在HashMap中Key的数据是根据hashcode来存的
HashMap是无序的,即我们插入和取出的顺序是不一样的
Students案例
由于是根据hashcode来存储key,我们需要重写students中的hashcode和equals方法
package Map_Key_Value;
public class Students {
private int age;
private String name;
public Students(int age, String name) {
super();
this.age = age;
this.name = name;
}
public int getAge() {
return age;
}
public String getName() {
return name;
}
@Override
public String toString() {
// TODO Auto-generated method stub
return "Student [ name: "+this.getName()+", age: "+this.getAge()+" ]";
}
@Override
public boolean equals(Object obj) {
if(this == obj) {
return true;
}
if(obj == null) {
return false;
}
if(obj instanceof Students) {
Students students = (Students) obj;
if(this.name.equals(students.name)&&this.age == students.age) {
return true;
}
}
return false;
}
@Override
public int hashCode() {
// TODO Auto-generated method stub
//第一种转换string的方法
// String age = String.valueOf(this.age);
//第二种
// String age = this.age+"";
String age = Integer.toString(this.age);
return age.hashCode()*this.name.hashCode();
}
}
package Map_Key_Value;
import java.util.HashMap;
import java.util.Map.Entry;
public class OriginalMap {
public static void main(String[] args) {
HashMap<Students, String> container = new HashMap<>();
// Students Jim = new Students(18, "Jim");
// container.put(Jim, "CSSE120");
container.put(new Students(18, "Jim"), "CSSE120");
container.put(new Students(20, "John"), "CSSE220");
container.put(new Students(17, "Alex"), "MA112");
container.put(new Students(20, "Eric"), "ESL102");
System.out.print("获取 Students(18, \"Jim\") 的值:");
System.out.println(container.get(new Students(18, "Jim")));//get value
//遍历
//使用KeySet方法
System.out.println("\tKeySet遍历");
for(Students i:container.keySet()) {
System.out.println(i.getName()+"----"+container.get(i));
}
//使用EntrySet方法
System.out.println("\tEntrySet遍历");
// Set<Map.Entry<Students, String>> entrys = container.entrySet();
for(Entry<Students, String> i : container.entrySet()) {
System.out.println(i.getKey().getName()+"----"+i.getValue());
}
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rtUa3yt3-1613061611892)(C:\Users\23881\AppData\Roaming\Typora\typora-user-images\image-20210208215330562.png)]
TreeMap
TreeMap存储数据根据compareTo方法,对于自定义对象需要override比较方法,这个与TreeSet类似
下面就不多解释了,不懂可以查看API
API: https://www.runoob.com/manual/jdk1.6/
JAVA常用类
java中还有一些常用的工具类,这些类都是日常开发时可能会用到的
Arrays类
Arrays是数组的意思,Arrays是为数组所开发的工具类(方法是静态static方法)
arrays能处理的类型有int, double, long, char等
常用方法 | 解释 |
---|---|
static boolean equals(int[ ] a, int[ ] a2) | 如果两个int类型数组相等,则返回true |
static int hashCode(int[ ] a) | 返回指定数组的哈希值 |
static void sort(double[ ] a) | 对指定数组进行升序排序 |
static String toString(char[ ] a) | 将数组内容转化为字符串 |
static void sort(T[ ] a, Comparator<? super T> c ) | 根据指定比较器的顺序对指定对象数组进行排序 |
Date和Calendar类
Data和Calendar是java中表示时间的两个类,它们能返回当前的时间的毫秒值
毫秒值表示自 1970 年 1 月 1 日 00:00:00 GMT 以来经过的毫秒数。
Date
构造方法
//以当前时间来初始化对象
Date date = new Date();
//构造函数接收一个参数,这个参数是从1970年起的毫秒数
Date date = new Date(long millisec)
常用方法 | 解释 |
---|---|
String toString() | 格式化日期转义形式yyyy-mm-dd的日期 |
int getHours() | 获取此刻的小时 |
String toLocaleString() | 返回当前本地格式的时间(方法已过时) |
package DateAndCalendar;
import java.util.Date;
public class DateDemo1 {
public static void main(String[] args) {
Date date = new Date();
System.out.println(date.toString());
System.out.println(date.toLocaleString());
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZHhV2f1Y-1613061611892)(C:\Users\23881\AppData\Roaming\Typora\typora-user-images\image-20210209130751457.png)]
我们可以使用SimpleDateFormat将日期输出格式化
格式 | 含义 | 例子 |
---|---|---|
yyyy | 年 | 2020 |
MM | 月 | 1 |
dd | 日 | 22 |
HH | 24小时制 | 13 |
hh | 12小时制 | 1 |
ss | 秒 | 07 |
S | 毫秒 | 7000 |
E | 星期几 | Monday |
D | 一年中的第几天 | 365 |
F | 一个月中的第几周的周几 | 2 |
w | 一年中的第几周 | 7 |
W | 一个月中的第几周 | 2 |
a | AM / PM | PM |
z | 时区 | CST |
package DateAndCalendar;
import java.text.SimpleDateFormat;
import java.util.Date;
public class DateDemo1 {
public static void main(String[] args) {
Date date = new Date();
// System.out.println(date.toString());
// System.out.println(date.toLocaleString());
SimpleDateFormat Df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
System.out.println(Df.format(date));
}
}
我们还能用时间来做一个定时器
package DajavateAndCalendar;
//在10s之内,持续打印hello, 结束时打印world
public class DateDemo1 {
public static void main(String[] args) {
long start = System.currentTimeMillis();
while(true) {
long end = System.currentTimeMillis();
if((end-start)>10000) {
System.out.println("world");
break;
}
System.out.println("hello");
}
}
}
Calendar
通过Calendar我们可以获取时间中特定的数据
Calendar类的功能要比Date类强大很多,而且在实现方式上也比Date类要复杂一些。
由于Calendar是protected类型,只能通过其他方法进行构建
static Calendar getInstance()
//使用默认时区获取日历
Calendar date = Calendar.getInstance();
在Calendar中,系统定义了一些常量
Calendar.YEAR 年
Calendar.MONTH 月
Calendar.DATE 日期
Calendar.HOUR 小时
Calendar.MINUTE 分钟
Calendar.SECOND 秒
Calendar.DAY_OF_WEEK 星期几
常用方法
int get(int field)
calendar.get(Calendar.YEAR)//可用于获取calendar特定的年-月-日
abstract void add(int field, int amount) //根据日历给特定的字段增减时间量
void set(int year, int month, int day, int hourOfDay, int minute, int second)//设置特定值
例子
package DateAndCalendar;
import java.util.Calendar;
import java.util.Date;
public class CalendarDemo1 {
public static void main(String[] args) {
Calendar calendar = Calendar.getInstance();
System.out.println(calendar.getTime());
int year = calendar.get(Calendar.YEAR);
int month = calendar.get(Calendar.MONTH)+1;
int day = calendar.get(Calendar.DAY_OF_MONTH);
System.out.println("Today is "+year+"-"+month+"-"+day);
}
}
JAVA异常处理
在写程序时我们可能会遇到程序报错的情况,此时编译器会出现红色的提示,如下
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jgGqCjwp-1613061611893)(C:\Users\23881\AppData\Roaming\Typora\typora-user-images\image-20210209194701662.png)]
这就是异常Exception
所有的异常都是从java.lang.Exception中继承的,Exception类是Throwable类的子类,Throwable还有一个子类Error
常见异常
- 用户输入了非法数据
- 要打开的文件不存在
- 网络连接通信时中断,或者JVM内存溢出
三大类异常
1. 检查性异常(checked exception)
如用户输入异常或者打开一个不存在的文件(这些异常在编译时不能被简单地忽略)
2. 运行时异常
运行时可能被程序员避免的异常
3. 错误
错误不是异常,通常是摆脱程序员控制的问题,例如当内存溢出时,会报错误
检查性异常
- InterruptedException
一个线程被另一个线程中断,抛出该异常。 - NoSuchFieldException
请求的变量不存在
非检查性异常
1. ArithmeticException
2. NumberFormatException
当应用程序试图将字符串转换成一种数值类型,但该字符串不能转换为适当格式时,抛出该异常
3. NullPointerException
4. IndexOutOfBoundsException
遍历超过
5. UnsupportedOperationException
捕获异常
//捕获异常的基础语法
try{
}catch(IOException e1){
//如何处理该异常,异常e1的类型可以为Exception的子类或Exception;
}catch(ArithmeticException e2){
}finally{
//finally可加可不加
//如果加了,无论是否发生异常,finally 代码块中的代码总会被执行。
//一般这里用于释放内存,如input.close()
}
例子
我们要用户输入两个数并互除,在除法中,被除数不能为0,所以我们要捕获这个异常
package Exception;
import java.util.Scanner;
public class ExceptionStudyDemo1 {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
try {
System.out.print("Num1: ");
int num = input.nextInt();
System.out.print("Num2: ");
int num2 = input.nextInt();
//注意此时num/num2得出的数字是整数
System.out.println("Num1 / Num2 = "+num/num2);
}
catch(ArithmeticException a) {
System.out.println(a.getMessage()); //获取为什么错误
}
finally {
input.close();
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HCb0goWT-1613061611894)(C:\Users\23881\AppData\Roaming\Typora\typora-user-images\image-20210211155159674.png)]
解决方法
添加一个
double rst = num*1.0/num2;java
package Exception;
import java.util.Scanner;
public class ExceptionStudyDemo1 {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
try {
System.out.print("Num1: ");
int num = input.nextInt();
System.out.print("Num2: ");
int num2 = input.nextInt();
double rst = num*1.0/num2;
System.out.println("Num1 / Num2 = "+rst);
}
catch(ArithmeticException a) {
System.out.println(a.getMessage());
}
finally {
input.close();
}
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qNDO2WYq-1613061611895)(C:\Users\23881\AppData\Roaming\Typora\typora-user-images\image-20210211155458450.png)]
Throws 关键字
throws中文意思是抛出,当一个函数有一个异常,此时我们不想处理时,我们可以throws这个Exception,交给函数外的部分处理
还是刚才num / num2的例子
package Exception;
import java.util.Scanner;
public class ExceptionStudyDemo1 {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
try {
System.out.print("Num1: ");
int num = input.nextInt();
System.out.print("Num2: ");
int num2 = input.nextInt();
divide(num, num2);
}
catch(ArithmeticException a) {
System.out.println(a.getMessage());
}
finally {
input.close();
}
}
public static void divide(int num, int num2) throws ArithmeticException{
System.out.println("Num1 / Num2 = "+num/num2);
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VOPcrIp5-1613061611895)(C:\Users\23881\AppData\Roaming\Typora\typora-user-images\image-20210211160006853.png)]
自定义异常类
所有异常必须是Throwable的子类。如果希望写一个检查性异常,则需要继承Exception类;如果是运行时异常,则继承RuntimeException类
继承语法
class CustomException extends Exception{
//里面根据编译器自动生成
}
package Exception;
import java.util.Scanner;
public class ExceptionStudyDemo1 {
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
try {
System.out.print("Num1: ");
int num = input.nextInt();
System.out.print("Num2: ");
int num2 = input.nextInt();
divide(num, num2);
}
catch(CustomException a){
//捕获自定义异常
}
public static void divide(int num, int num2) throws CustomException{ //在方法中抛出异常
if(num2 == 0) {
throw new CustomException(); //在方法中加入此语句,即为判断那种情况下抛出自定义异常
}
}
}
自定义异常有什么用呢?
当我们自己开发时,有时需要自己定义一些异常(别人能看懂的异常类名字),这样可以方便开发
JAVA IO处理
我们每次运行一个程序,此时我们输入的数据仅仅是存在缓存中的,倘若我们关掉程序,数据就会随之消失。为了把数据存起来,我们需要学习IO处理(Input Output),这样数据才可以写入内存硬盘里面。
流的概念
流stream可以为数据与目的之间建立通道。通过流我们可以把在内存中的信息写入到硬盘中;同理,我们也可以将硬盘中的数据读取,print在控制台里面
通过流读取的不管是字节还是字符,一般返回的都是int值
流的分类
流有很多种类,按照处理数据侧重点的不同可以分为输入/输出字节流与输入/输出字符流;
这里我引用某位博主的图片来解释:https://blog.youkuaiyun.com/mu_wind/article/details/108674284
字节流
字节流每次读取或写入一个字节,2字符=1字节, 1字节(Byte) = 8位 (bit)。字节流可以处理图像,视频等
一般英文字母一个字母为一个字节(字节是以int类型表示而字母则是以char类型表示)
字符流
字符流每次读取或写入一个字符。字符流只能用于处理文字信息
中文与英文有点区别,1个中文代表1个字符(char),1个字符 = 2个字节, 加入用字节流读取中文字符会出现乱码
流的使用
字节流使用
输入字节流
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wjOhF8K1-1613061611896)(C:\Users\23881\AppData\Roaming\Typora\typora-user-images\image-20210211224816778.png)]
//实例化对象
FileInputStream file = new FileInputStream("E:\\java程序\\ReadFile.txt");
//读取文件内容
//读取出来的是字节流信息,要转化成char或string才能打印
//file.read()一次读取一个字节,如果有返回此字节,若没有返回-1
int data;
while((data = file.read()) != -1) {
System.out.print((char)data);
}
//使用自制缓冲区读取数据
byte[] buffer = new byte[3];//自制的一次能储存3个字节的缓冲区
int count=0;
while((count = file.read(buffer))!=-1) {
System.out.print(new String(buffer,0,count));
}
//注意打开的文件使用后要关闭
file.close();
输出字节流
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DSBb74TV-1613061611898)(C:\Users\23881\AppData\Roaming\Typora\typora-user-images\image-20210211224920029.png)]
//实例化对象
//true表示如果给出的文件地址已存在,则在此文件的基础上再写如内容
FileOutputStream Out = new FileOutputStream("E:\\java程序\\OutPutFile.txt",true);
//写入内容
String words = "HELLO WORLD ";
Out.write(words.getBytes()); //通过字节流来写入,必须是字节形式
//同样,写完记得关闭
Out.close();
由于普通的输入输出字节流效率不高,一般需要与缓冲流相互配合
文件内容先读入缓冲区,接着被程序访问,这样可以减少程序访问硬盘的次数
缓冲输入输出流
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tZ7vrIER-1613061611899)(C:\Users\23881\AppData\Roaming\Typora\typora-user-images\image-20210211225026578.png)]
//使用缓冲区读取数据
FileInputStream file = new FileInputStream("E:\\java程序\\ReadFile.txt");
BufferedInputStream buf = new BufferedInputStream(file);
int data;
while((data = buf.read()) != -1) {
System.out.print((char)data);
}
//缓冲区用完也最好记得关闭
buf.close();
//使用缓冲区输出数据
package IOStudy;
import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
public class buf {
public static void main(String[] args) throws Exception {
System.out.println("hello world");
FileOutputStream file = new FileOutputStream("E:\\java程序\\BufferOutput.txt",true);
BufferedOutputStream buf = new BufferedOutputStream(file);
for(int i=0;i<4;i++) {
buf.write("hello world ".getBytes());
buf.flush(); //使用缓冲区每写入一次数据要刷新一遍
}
System.out.println("Finish");
buf.close();
}
}
对象流与序列化
字节流可以传输任何数据类型,包括对象,由此产生了另一个流,对象流
创建方法
ObjectOutputStream obj = new ObjectOutputStream(new FileOutputStream(source, true));
ObjectInputStream 读取Obj数据顺序根据写入顺序来,先进先出
在使用对象流写入对象数据时,该对象必须实现Serializable接口
之前接口部分有提到Serializable没有任何方法和常量,它是一个标记类
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SMPV6LNC-1613061611899)(C:\Users\23881\AppData\Roaming\Typora\typora-user-images\image-20210211225831552.png)]
下面我们要将两个学生类的信息储存到硬盘中,再通过一个程序将内容读取出来
//学生类,要实现Serializable接口
package IOStudy;
import java.io.Serializable;
public class Student implements Serializable{
/**
*
*/
private static final long serialVersionUID = -4870784691100550826L;
private String name;
private int age;
public Student(String name, int age) {
super();
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;
}
@Override
public String toString() {
return "["+this.getName()+" , "+this.getAge()+"]";
}
}
//写入信息
package IOStudy;
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
public class ObjectWrite {
public static void main(String[] args) throws Exception{
String source = "E:\\java程序\\ObjWriter.bin";
ObjectOutputStream obj = new ObjectOutputStream(new FileOutputStream(source));
Student Biden = new Student("Biden", 78);
Student Trump = new Student("Trump", 72);
obj.writeObject(Trump);
obj.writeObject(Biden);
obj.writeObject(null);
obj.close();
System.out.println("Finish");
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3t55wuuK-1613061611900)(C:\Users\23881\AppData\Roaming\Typora\typora-user-images\image-20210212000040489.png)]
//读取信息
package IOStudy;
import java.io.FileInputStream;
import java.io.ObjectInputStream;
import java.util.ArrayList;
public class ObjectRead {
public static void main(String[] args) throws Exception{
String source ="E:\\java程序\\ObjWriter.bin";
ObjectInputStream obj = new ObjectInputStream(new FileInputStream(source));
Object stu;
while((stu=obj.readObject())!= null) {
System.out.println(stu);
}
obj.close();
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5g4mgD9P-1613061611901)(C:\Users\23881\AppData\Roaming\Typora\typora-user-images\image-20210212000057958.png)]
字符流使用
字符流输入输出
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Q0B1QXiv-1613061611901)(C:\Users\23881\AppData\Roaming\Typora\typora-user-images\image-20210211225130652.png)]
//实例化对象
FileReader read = new FileReader(Source);
//读取操作与字节流类似
//案例(反例),我们使用默认的解码格式读取数据
package IOStudy;
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.InputStreamReader;
public class ReadFile2 {
public static void main(String[] args) throws Exception{
String Source = "E:\\java程序\\字符Reader.txt";
FileReader read = new FileReader(Source);
int count =0;
while((count = read.read())!= -1) {
System.out.print((char)count);
}
read.close();
}
}
结果,中文出现乱码
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9Mfg0ljR-1613061611903)(C:\Users\23881\AppData\Roaming\Typora\typora-user-images\image-20210211232655649.png)]
由于英文一个字母只占一个字节,默认的GBK解码没有问题。但是中文一个字占3个字节,使用GBK解码(与文本保存的格式不同)出现了乱码。
为了保证能正确解码,我们读取字符文件时要指定解码格式,例如UTF-8
//下面是对上面例子的修改
package IOStudy;
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.InputStreamReader;
public class ReadFile2 {
public static void main(String[] args) throws Exception{
String Source = "E:\\java程序\\字符Reader.txt";
InputStreamReader read = new InputStreamReader(new FileInputStream(Source),"UTF-8");
int count =0;
while((count = read.read())!= -1) {
System.out.print((char)count);
}
}
}
结果
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cmLS8dzw-1613061611903)(C:\Users\23881\AppData\Roaming\Typora\typora-user-images\image-20210211233135453.png)]
中文字符流写出时,不用担心上面编码的问题,系统默认是GBK编码,例子如下
package IOStudy;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.OutputStreamWriter;
public class WriteFile2 {
public static void main(String[] args) throws Exception{
String Source = "E:\\java程序\\字符Writer.txt";
FileWriter file = new FileWriter(Source);
file.write(" 世界你好 ");
System.out.println("Finish");
file.close();
}
}
写入的结果
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Fl8HL4f9-1613061611903)(C:\Users\23881\AppData\Roaming\Typora\typora-user-images\image-20210211233838187.png)]
ANSI就是GBK编码格式
当然,我们也可以指定输出格式,像输入一样
package IOStudy;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.OutputStreamWriter;
public class WriteFile2 {
public static void main(String[] args) throws Exception{
String Source = "E:\\java程序\\字符Writer.txt";
OutputStreamWriter file = new OutputStreamWriter(new FileOutputStream(Source,true), "UTF-8");
// FileWriter file = new FileWriter(Source);
file.write(" 世界你好 ");
System.out.println("Finish");
file.close();
}
}
结果:文件使用UTF-8编码格式
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yYUtAIF2-1613061611904)(C:\Users\23881\AppData\Roaming\Typora\typora-user-images\image-20210211234107217.png)]
字符流读取缓冲流
package IOStudy;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.InputStreamReader;
public class BufferCharRead {
public static void main(String[] args) throws Exception{
String source = "E:\\编程.txt";
BufferedReader buf = new BufferedReader(new InputStreamReader(new FileInputStream(source), "GBK"));
for(int i=0;i<20;i++)//打印缓冲区中的前20行
//buf.readLine()指读一行数据
System.out.println(buf.readLine());
buf.close();
}
}
文件夹操作
java的IO处理还包括文件夹的一些操作,例如文件夹的创建,删除等
//构造方法
//--1--
File file = new File(String parent_path, String child_path);
//--2--
File file = new File(String absolute_path)
目录操作 | 解释 |
---|---|
createNewFile() | 创建一个新文件,不是文件夹 |
mkdir() | 创建一个新目录 |
delete() | 删除文件或空目录 |
exists() | 判断File对象所表示的对象是否存在 |
getAbsolutePath() | 获取文件的绝对路径 |
getName() | 获取目录的名字 |
getParent() | 获取文件/目录所在的目录, 该文件夹所在绝对路径E:\java程序\PicSource\Summer_Pocket Parent就是E:\java程序\PicSource Child就是Summer_Pocket |
isDirectory() | 判断是否是目录 |
isFile() | 判断是否是文件 |
length() | 获取文件的长度,以字节为单位 |
renameTo() | 修改文件名为 |
listFiles() | 列出目录中的所有内容 File[] files = file.listFiles(); 返回的是File类型数组 File[] file2 = file.listFiles(FileFilter filter);//可以通过Filefilter进行过滤文件的目的 |
FileFilter接口
public interface FileFilter
//需要实现的方法
boolean accept(File pathname)
案例
存了一些图片在某个文件夹中,我们要将这些图片重命名,按1-50的顺序重命名
package IOStudy;
import java.io.File;
import java.io.FileFilter;
import java.util.Scanner;
public class FileOperator {
public static void main(String[] args) throws Exception {
File file = new File("E:\\java程序\\PicSource");
System.out.println("dictory's name: "+file.getName());
File[] files = file.listFiles();
Integer count =0;
for(File pic:files) {
File image = new File(file.getAbsolutePath(),count.toString()+".jpg");
if(pic.isFile()) {
pic.renameTo(image);
count++;
}
}
File[] file2 = file.listFiles(new FileFilter() {
@Override
public boolean accept(File pathname) {
if(pathname.getName().endsWith(".jpg"))
return true;
return false;
}
});
for(File pic:file2) {
System.out.println(pic.getName());
}
}
}
结果
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fqe9AdYz-1613061611905)(C:\Users\23881\AppData\Roaming\Typora\typora-user-images\image-20210212002426056.png)]
Properties
properties是一个属性集合,继承HashTable,是一个线程安全的集合;properties可以储存键值对,同时键值对是字符串类型,它没有泛型。
//构造方法
Properties container = new Properties();
//常用方法
container.setProperty("Trump","72");
//使用迭代器遍历properties中的键值对
Iterator<Entry<Object, Object>> itr = container.entrySet().iterator();
while(itr.hasNext()) {
Entry<Object, Object> set = itr.next();
System.out.println(set.getKey()+"=====>"+set.getValue());
}
//使用Set遍历键值对
Set<String> set = container.stringPropertyNames();
for(String pro:set) {
System.out.println(pro+"=====>"+container.getProperty(pro));
}
//load加载存储在硬盘中property的内容
container.load(FileInputStream fis);
//以适合的方法将键值对写入到输出流,可以保存注释
container.store(FileOutputStream fos, String comment);
//-----//
//store方法,以适合的方法将键值对写入到输出流,可以保存注释
FileOutputStream fos = new FileOutputStream("E:\\java程序\\properties_save.txt");
container.store(fos, "American President");
fos.close();
//----//
//将属性列表输出到指定的输出流
container.list(PrintWriter file);
举一个储存properties的例子
package IOStudy;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.util.Iterator;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.Set;
public class PropertyStudy {
public static void main(String[] args) throws Exception {
Properties container = new Properties();
container.setProperty("Trump", "72");
container.setProperty("Biden", "74");
System.out.println(container.getProperty("Trump"));
//使用迭代器遍历
Iterator<Entry<Object, Object>> itr = container.entrySet().iterator();
while(itr.hasNext()) {
Entry<Object, Object> set = itr.next();
System.out.println(set.getKey()+"=====>"+set.getValue());
}
//将属性列表输出到指定的输出流
PrintWriter file =new PrintWriter(new FileOutputStream("E:\\java程序\\properties_save.txt"));
container.list(file);
file.close();
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qPwJjFAh-1613061611907)(C:\Users\23881\AppData\Roaming\Typora\typora-user-images\image-20210212000516493.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rRe7NZqH-1613061611908)(C:\Users\23881\AppData\Roaming\Typora\typora-user-images\image-20210212000544377.png)]
读取properties在硬盘中的内容
//load加载properties内容
FileInputStream fis = new FileInputStream("E:\\java程序\\properties_save.txt");
container.load(fis);
fis.close();
Set<String> set = container.stringPropertyNames();
for(String pro:set) {
System.out.println(pro+"=====>"+container.getProperty(pro));
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2oIxvIii-1613061611908)(C:\Users\23881\AppData\Roaming\Typora\typora-user-images\image-20210212000811298.png)]
参考文献
API:https://www.runoob.com/manual/jdk1.6/
菜鸟教程:https://www.runoob.com/java/java-tutorial.html
JAVA final关键字:https://blog.youkuaiyun.com/PickUpOldDriver/article/details/80566628
JAVA static block:https://blog.youkuaiyun.com/qq_35868412/article/details/89360250
JAVA this关键字:https://blog.youkuaiyun.com/qq_43555323/article/details/84993460
JAVA IO:https://blog.youkuaiyun.com/baobeiSimple/article/details/1713797
BufferedReader(new InputStreamReader(new FileInputStream(source), “GBK”));
for(int i=0;i<20;i++)//打印缓冲区中的前20行
//buf.readLine()指读一行数据
System.out.println(buf.readLine());
buf.close();
}
}
### 文件夹操作
java的IO处理还包括文件夹的一些操作,例如文件夹的创建,删除等
```java
//构造方法
//--1--
File file = new File(String parent_path, String child_path);
//--2--
File file = new File(String absolute_path)
目录操作 | 解释 |
---|---|
createNewFile() | 创建一个新文件,不是文件夹 |
mkdir() | 创建一个新目录 |
delete() | 删除文件或空目录 |
exists() | 判断File对象所表示的对象是否存在 |
getAbsolutePath() | 获取文件的绝对路径 |
getName() | 获取目录的名字 |
getParent() | 获取文件/目录所在的目录, 该文件夹所在绝对路径E:\java程序\PicSource\Summer_Pocket Parent就是E:\java程序\PicSource Child就是Summer_Pocket |
isDirectory() | 判断是否是目录 |
isFile() | 判断是否是文件 |
length() | 获取文件的长度,以字节为单位 |
renameTo() | 修改文件名为 |
listFiles() | 列出目录中的所有内容 File[] files = file.listFiles(); 返回的是File类型数组 File[] file2 = file.listFiles(FileFilter filter);//可以通过Filefilter进行过滤文件的目的 |
FileFilter接口
public interface FileFilter
//需要实现的方法
boolean accept(File pathname)
案例
存了一些图片在某个文件夹中,我们要将这些图片重命名,按1-50的顺序重命名
package IOStudy;
import java.io.File;
import java.io.FileFilter;
import java.util.Scanner;
public class FileOperator {
public static void main(String[] args) throws Exception {
File file = new File("E:\\java程序\\PicSource");
System.out.println("dictory's name: "+file.getName());
File[] files = file.listFiles();
Integer count =0;
for(File pic:files) {
File image = new File(file.getAbsolutePath(),count.toString()+".jpg");
if(pic.isFile()) {
pic.renameTo(image);
count++;
}
}
File[] file2 = file.listFiles(new FileFilter() {
@Override
public boolean accept(File pathname) {
if(pathname.getName().endsWith(".jpg"))
return true;
return false;
}
});
for(File pic:file2) {
System.out.println(pic.getName());
}
}
}
结果
[外链图片转存中…(img-fqe9AdYz-1613061611905)]
Properties
properties是一个属性集合,继承HashTable,是一个线程安全的集合;properties可以储存键值对,同时键值对是字符串类型,它没有泛型。
//构造方法
Properties container = new Properties();
//常用方法
container.setProperty("Trump","72");
//使用迭代器遍历properties中的键值对
Iterator<Entry<Object, Object>> itr = container.entrySet().iterator();
while(itr.hasNext()) {
Entry<Object, Object> set = itr.next();
System.out.println(set.getKey()+"=====>"+set.getValue());
}
//使用Set遍历键值对
Set<String> set = container.stringPropertyNames();
for(String pro:set) {
System.out.println(pro+"=====>"+container.getProperty(pro));
}
//load加载存储在硬盘中property的内容
container.load(FileInputStream fis);
//以适合的方法将键值对写入到输出流,可以保存注释
container.store(FileOutputStream fos, String comment);
//-----//
//store方法,以适合的方法将键值对写入到输出流,可以保存注释
FileOutputStream fos = new FileOutputStream("E:\\java程序\\properties_save.txt");
container.store(fos, "American President");
fos.close();
//----//
//将属性列表输出到指定的输出流
container.list(PrintWriter file);
举一个储存properties的例子
package IOStudy;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.util.Iterator;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.Set;
public class PropertyStudy {
public static void main(String[] args) throws Exception {
Properties container = new Properties();
container.setProperty("Trump", "72");
container.setProperty("Biden", "74");
System.out.println(container.getProperty("Trump"));
//使用迭代器遍历
Iterator<Entry<Object, Object>> itr = container.entrySet().iterator();
while(itr.hasNext()) {
Entry<Object, Object> set = itr.next();
System.out.println(set.getKey()+"=====>"+set.getValue());
}
//将属性列表输出到指定的输出流
PrintWriter file =new PrintWriter(new FileOutputStream("E:\\java程序\\properties_save.txt"));
container.list(file);
file.close();
}
}
[外链图片转存中…(img-qPwJjFAh-1613061611907)]
[外链图片转存中…(img-rRe7NZqH-1613061611908)]
读取properties在硬盘中的内容
//load加载properties内容
FileInputStream fis = new FileInputStream("E:\\java程序\\properties_save.txt");
container.load(fis);
fis.close();
Set<String> set = container.stringPropertyNames();
for(String pro:set) {
System.out.println(pro+"=====>"+container.getProperty(pro));
}
[外链图片转存中…(img-2oIxvIii-1613061611908)]
参考文献
API:https://www.runoob.com/manual/jdk1.6/
菜鸟教程:https://www.runoob.com/java/java-tutorial.html
JAVA final关键字:https://blog.youkuaiyun.com/PickUpOldDriver/article/details/80566628
JAVA static block:https://blog.youkuaiyun.com/qq_35868412/article/details/89360250
JAVA this关键字:https://blog.youkuaiyun.com/qq_43555323/article/details/84993460
JAVA IO:https://blog.youkuaiyun.com/baobeiSimple/article/details/1713797