[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0G1gJPU5-1678672773976)(C:\Users\ADMIN\AppData\Roaming\Typora\typora-user-images\image-20220712172429817.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aaPqzpC3-1678672773978)(C:\Users\ADMIN\AppData\Roaming\Typora\typora-user-images\image-20220712172924460.png)]
JAVA语言是面向对象的(oop)
JAVA语言是见状的。JAVA的强类型机制、异常处理、垃圾的自动手机是JAVA程序健壮性的重要保证
JAVA是跨平台性质的。【.class文件通过解释器可在多个系统下运行】
JAVA语言是解释型的[了解]
解释性语言:JavaScript,PHP,Java
编译型语言:c、c++
区别:解释性语言,编译后的代码不能被机器直接执行,需要解释器来执行,编译型语言,编译后的代码已经是二进制码可以直接被机器直接运行
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HNPNHfBw-1678672773979)(C:\Users\ADMIN\AppData\Roaming\Typora\typora-user-images\image-20220712174645511.png)]
JVM:Java虚拟机,实现在不同系统解释统一代码
JRE(JAVA Runtime Envirnment JAVA运行环境)
JRE = JVM + JAVA的核心类库【类】
包括Java虚拟机和JAVA程序所需的核心类库等,如果想要运行一个开发好的Java程序,计算机中只需要安装JRE即可
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HUhn2VCT-1678672773980)(C:\Users\ADMIN\AppData\Roaming\Typora\typora-user-images\image-20220712193421703.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fAhqugD5-1678672773982)(C:\Users\ADMIN\AppData\Roaming\Typora\typora-user-images\image-20220712193511933.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4ct08jOP-1678672773983)(C:\Users\ADMIN\AppData\Roaming\Typora\typora-user-images\image-20220712193641270.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7MA3Z3S6-1678672773985)(C:\Users\ADMIN\AppData\Roaming\Typora\typora-user-images\image-20220712193746905.png)]
8.Public类的名称需要与源文件名称相同
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vMu97UHe-1678672773986)(C:\Users\ADMIN\AppData\Roaming\Typora\typora-user-images\image-20220712194121604.png)]
每一个类编译后,都对应一个.class(JVM是加载机制,加载每个类)
Java 支持三种注释方式。分别是
单号注释 //
多行注释 /* */
文档注释,它以 /** 开始,以 */结束。
可以使用 javadoc 工具软件来生成信息,并输出到HTML文件中。
在开始的 /**
之后,第一行或几行是关于类、变量和方法的主要描述
。之后,你可以包含一个或多个各种各样的 @ 标签
。每一个 @ 标签必须在一个新行的开始或者在一行的开始紧跟星号(*).
下面是一个类的说明注释的实例:
/*** 这个类绘制一个条形图
* @author runoob
* @version 1.2
*/
javadoc 工具将你 Java 程序的源代码作为输入,输出一些包含你程序注释的HTML文件。
每一个类的信息将在独自的HTML文件里。javadoc 也可以输出继承的树形结构和索引。
由于 javadoc 的实现不同,工作也可能不同,你需要检查你的 Java 开发系统的版本等细节,选择合适的 Javadoc 版本。
四.JavaDoc注释常用标签
1.@see
使用@see时应注意 写在每行注释的开头。
用法:@see 类#属性 / 方法
JavaDoc注解中的{@link}与@see
,他可以链接类或者方法,方便自己或别人看的时候,可以直接找到你关联的代码类或者啥的。
在编辑器中,按住
ctrl+鼠标左键
,就能直接跳转
/**
* {@link ClassA#属性}
* {@link ClassA#方法}
* {@link com.joh.demo.ClassB#方法}
*
* 可不用开头写 {@link ClassA#属性}
* 可不用开头写 {@link ClassA#方法}
* 可不用开头写 {@link com.joh.demo.ClassB#方法}
*/
JavaDoc注解中的{@link}与@see
,他可以链接类或者方法,方便自己或别人看的时候,可以直接找到你关联的代码类或者啥的。
在编辑器中,按住
ctrl+鼠标左键
,就能直接跳转
标签 | 描述 | 示例 |
---|---|---|
@author | 标识一个类的作者 | @author description |
@deprecated | 指名一个过期的类或成员 | @deprecated description |
{@docRoot} | 指明当前文档根目录的路径 | Directory Path |
@exception | 标志一个类抛出的异常 | @exception exception-name explanation |
{@inheritDoc} | 从直接父类继承的注释 | Inherits a comment from the immediate surperclass. |
{@link} | 插入一个到另一个方法或者代码的链接,但是该链接显示链接样式 | Inserts an in-line link to another topic. |
{@linkplain} | 插入一个到另一个方法或者代码的链接,但是该链接显示纯文本字体 | Inserts an in-line link to another topic. |
@param | 说明一个方法的参数 | @param parameter-name explanation |
@return | 说明返回值类型 | @return explanation |
@serial | 说明一个序列化属性 | @serial description |
@serialData | 说明通过writeObject( ) 和 writeExternal( )方法写的数据 | @serialData description |
@serialField | 说明一个ObjectStreamField组件 | @serialField name type description |
@throws | 和 @exception标签一样.,说明要抛出的异常 | @throws tag has the same meaning as the @exception tag. |
{@value} | 显示常量的值,该常量必须是static属性。 | Displays the value of a constant, which must be a static field. |
@version | 指定类的版本 | @version info |
(29条消息) 【Java基础】JavaDoc注释标签大全_墩墩分墩的博客-CSDN博客_java注释标签
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dNjavjSM-1678672773988)(C:\Users\ADMIN\AppData\Roaming\Typora\typora-user-images\image-20220712235502004.png)]
API(Application Programming Interface,应用程序编程接口)是JAVA提供的基本编程接口(java提供的类还有相关方法)。
中文在线文档:https://www.matools.com
Java语言提供了大量基础类,因此Oracle公司也为这些基础类提供了相应的API文档,用于告诉开发者如何使用这些类,以及这些类李包含的方法。
Java类的组织形式
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JTic2UV9-1678672773989)(C:\Users\ADMIN\AppData\Roaming\Typora\typora-user-images\image-20220713141610471.png)]
举例说明如何使用ArrayList类有哪些方法
规则:必须要做
规范:最好这样做
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-h59lHSlo-1678672773990)(C:\Users\ADMIN\AppData\Roaming\Typora\typora-user-images\image-20220715200504867.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-J5C8uDAN-1678672773991)(C:\Users\ADMIN\AppData\Roaming\Typora\typora-user-images\image-20220715200807421.png)]
//1.public class hello表示hello是一个类,是一个public公有的类
//2.hello{ }表示一个类的开始和结束
//3.public static void main(string[]args)表示一个主方法,即程序入口
public class Hello {
//编写一个主方法
public static void main(String[] args){
System.out.println("hello,world_JAVA");
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PgskIzwi-1678672773993)(C:\Users\ADMIN\AppData\Roaming\Typora\typora-user-images\image-20220712200215383.png)]
public class ChangeChar{
public static void main(String[] args){
System.out.println("北京\t上海\t换行\n 符号\\ 一个\" 一个\' 回车\r回车完毕");
//解读
//1.输出 回车
//2.\r表示回车
//3.将后面的字符一个个替换成前面的字符
}
}
class Homework{
public static void main(String[] args){
System.out.println("书名\t作者\t价格\t销量\n三国\t罗贯中\t120\t1000");
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-S74iIkO6-1678672773994)(C:\Users\ADMIN\AppData\Roaming\Typora\typora-user-images\image-20220713113144123.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Dhn7Modn-1678672773995)(C:\Users\ADMIN\AppData\Roaming\Typora\typora-user-images\image-20220713113427422.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ddNvjOh0-1678672773996)(C:\Users\ADMIN\AppData\Roaming\Typora\typora-user-images\image-20220713114432925.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-t99WbG11-1678672773997)(C:\Users\ADMIN\AppData\Roaming\Typora\typora-user-images\image-20220713122822166.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-abX8fry9-1678672773998)(C:\Users\ADMIN\AppData\Roaming\Typora\typora-user-images\image-20220713140553571.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XEu4XD9q-1678672774000)(C:\Users\ADMIN\AppData\Roaming\Typora\typora-user-images\image-20220713151322638.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FPKSrFc1-1678672774001)(C:\Users\ADMIN\AppData\Roaming\Typora\typora-user-images\image-20220713152512264.png)]
细节说明
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-v8HXGI3U-1678672774002)(C:\Users\ADMIN\AppData\Roaming\Typora\typora-user-images\image-20220713154829300.png)]
简介
当java程序在进行赋值或者运算时,精度小的类型回自动转换为精度大的数据类型
这个就是自动类型转换**(注意是精度噢)**
数据类型按精度(容量)大小排序如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NCZ5hly3-1678672774003)(C:\Users\ADMIN\AppData\Roaming\Typora\typora-user-images\image-20220713182119602.png)]
例子:
int a='c';//对
double d=80;//对
需要注意的地方:
介绍:
自动类型转换的逆过程,将精度大的数据类型转换为精度小的数据类型。使用时要加上强制转换符(int/byte/float/…),但可能造成精度降低或溢出,格外要注意。
案例演示:
public class ForceConvert {
public static void main(String[] args){
int n1 = (int)1.9;
System.out.println("n1="+n1);//造成精度损失
//输出n1=1
int n2 = 2000;
byte b1 = (byte)n2;
System.out.println("b1="+b1);//造成数据溢出
//输出b1=-48
}
}
需要注意的细节:
当进行数据的精度从大—>小,就需要使用强制转换
强转换符号只针对最近的操作数有效,玩玩会使用小括号提升优先级
public class ForceConvertDetail {
public static void main(String[] args){
//演示强制类型转换
//强转符号只针对最近的操作数有效,往往回使用小括号提升优先级
// int x = (int)10 * 3.5 + 6 * 1.5;
//double -> int 错误,强制转换()只对10有效
int y = (int)(10 * 3.5 + 6 * 1.5);
//使用小括号提升优先级计算进行强制转换
System.out.println(y);
}
}
char类型可以保存int类型的常量值,但不能保存int的变量值,需要强转
char c1=100;//ok
int m=100;//ok
char c2 = m;//false
char c3 = (char)m;//ok
System.out.println(c3);
byte和short,char类型在进行运算时,当作int类型处理。
介绍
在程序开发中,我们经常需要将基本数据类型转换成String类型。或者将String类型转换成基本数据类型。
基本数据类型转String类型
语法:将基本类型的值+""即可
演示:
String类型转基本数据类型
语法:通过基本类型的包装类调用parseXX方法即可
演示:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NrbYR63y-1678672774005)(C:\Users\ADMIN\AppData\Roaming\Typora\typora-user-images\image-20220713204437534.png)]
注意:Char转换则使用ch.charAt(0)取出其中一个字符
注意事项:
运算符是一种特殊符号,用来表示数据的运算、赋值和比较等…
public class ArithmeticOperator {
public static void main(String[] args){
System.out.println(10/4);//从数学角度来讲是2.5,java运算结果是2
System.out.println(10.0/4);//java运算结果是2.5
double d = 10 / 4;//java钟10 / 4 = 2,2=>2.0
System.out.println(d);//结果是2.0
}
}
%的本质,公式:a % b = a - a / b * b
-10 % 3 = -10 - (-10)/3 * 3 = -10 - (-3) * 3 = -10 + 9 = -1
public class ArithmeticOperator {
public static void main(String[] args){
System.out.println(10 % 3);//1
System.out.println(-10 % 3);//-1
System.out.println(10 % -3);//
}
}
作为表达式使用
前++:++i先自增后赋值
后++:i++先赋值后自增此外还有**等
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PLr328cK-1678672774006)(C:\Users\ADMIN\AppData\Roaming\Typora\typora-user-images\image-20220715193140160.png)]
//原理如下
int i = 1;
k = i++;
/*
内部规则:
temp = i;
i = i + 1;
k = temp;
*/
//如果k变量换成i,则最后一步手指指着的k也改成i,所以结果仍未未自增前
k = ++i;
/*
内部规则:
i = i + 1;
temp = i;
k = temp;
*/
//与上同理
关系运算符的结果都是boolean,要么true,要么false
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lfffmlaz-1678672774008)(C:\Users\ADMIN\AppData\Roaming\Typora\typora-user-images\image-20220715194231467.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZtyuCIpX-1678672774009)(C:\Users\ADMIN\AppData\Roaming\Typora\typora-user-images\image-20220715194623760.png)]
短路与逻辑的区别:
- &&(短路与):从优先级最高开始,只要出现false,则后面的条件均不会判断,最终结果直接判定为false
- &(逻辑与):表达式内所有条件都需要判断
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vmRi5Eoo-1678672774012)(C:\Users\ADMIN\AppData\Roaming\Typora\typora-user-images\image-20220715195624684.png)]
条件表达式?表达式1:表达式2
介绍:
在编程钟,需要接收用户输入的数据,就可以使用键盘输入语句来获取。
使用一个扫描器(对象),就是Scanner
步骤:
演示
//1.引入Scanner类所在的包
import java.util.Scanner;//表示把java.util下的Scanner类导入
public class Input {
public static void main(String[] args){
//2.创建一个Scanner 对象,myScanner即对象
Scanner myScanner = new Scanner(System.in);
//3.接收用户的输入需要使用相关方法,如下的next(),nextInt(),nextDouble()
System.out.println("请输入名字");
String name = myScanner.next();
System.out.println("请输入年龄");
int age = myScanner.nextInt();
System.out.println("请输入薪水");
double sal = myScanner.nextDouble();
System.out.println("人的信息如下:");
System.out.println("name=" + name + " age=" + age + " salary=" + sal);
}
}
public static void main(String[] args){
//1 =》00000000 00000000 00000000 00000001
//=》00000000 00000000 00000000 00000000
//本质1/2/2 = 0
int a = 1 >> 2;//1向右位移2位
//-1 =》 10000001 =》11111110(反码) =》 11111111(补码) =》 操作11111111 =》反码11111110 =》 原码10000001 ->-1
int b = -1 >> 2;
//1 = 》00000001 =》 00000100 本质1*2*2
int c = 1 << 2;
int e = 3 >>> 2;
//a,b,c,d,e结果是多少
}
>>
:低位溢出,符号位不变,并用符号位补溢出高位<<
:符号位不变,低位补0>>>
逻辑右移也叫无符号右移,规则:低位溢出,高位补0<<<
符号public static void main(String[] args){
//得到2的补码,2的原码:00000000 00000000 00000000 00000010
//2的补码:00000000 00000000 00000000 00000000 00000010
//3的原码:00000000 00000000 00000000 00000000 00000011
//3的补码:00000000 00000000 00000000 00000000 00000011
//按位与&:
//00000000 00000000 00000000 00000010 <- 2
//00000000 00000000 00000000 00000011 <- 3
//00000000 00000000 00000000 00000010 <- result = 2
System.out.println(2&3);//2
//-2的原码:10000000 00000000 00000000 00000010
//-2的反码:11111111 11111111 11111111 11111101
//-2的补码:11111111 11111111 11111111 11111111
//~-2操作:00000000 00000000 00000000 00000001 -< result = 1
System.out.println(~-2);//1
//2的原码:00000000 00000000 00000000 00000010
//2的补码:00000000 00000000 00000000 00000010
//~2操作:11111111 11111111 11111111 11111101 <-result(补码)
//因为不是正数,所以本身并不是三码合一,需要由补码转原码
//运算后的反码:11111111 11111111 11111111 11111100
//运算后的原码:10000000 00000000 00000000 00000011 <-result = -3
System.out.println(~2);//-3
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aOIfjy5L-1678672774013)(C:\Users\ADMIN\AppData\Roaming\Typora\typora-user-images\image-20220717123127845.png)]
用法:
推荐在单个值进行选择的情况下使用switch,在多个区间的情况下使用if
import java.util.Scanner;
public class Switch01 {
public static void main(String[] args) {
Scanner myScanner = new Scanner(System.in);
System.out.println("请输入一个字符(a-g)");
char c1 = myScanner.next().charAt(0);
switch (c1) {
case 'a':
System.out.println("今天星期1,猴子穿新衣");
break;
case 'b':
System.out.println("今天星期2,猴子穿新衣");
break;
case 'c':
System.out.println("今天星期3,猴子穿新衣");
break;
default:
System.out.println("你输入的字符不正确,没有匹配");
break;//可以没有!
}
System.out.println("退出switch,继续执行程序");
}
}
需要注意的细节:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9fMlyM5W-1678672774014)(C:\Users\ADMIN\AppData\Roaming\Typora\typora-user-images\image-20220717173445163.png)]
说明:
使用String类中的equals()方法
方式1-动态初始化:
数据类型 数组名[] = new 数据类型[大小]
int a[] = new int[5];//创建了一个数组,名字a,存放5个int
方式2-动态初始化:
先声明数组
语法:数据类型 数组名[];
也可以 数据类型[] 数组名;
此时并没有开辟内存空间,a指向null,会报错(空指针异常)
int a[];
int[] a;
创建数组
语法:数组名 = new 数据类型[大小];
a = new int[10]
方式3-静态初始化:
初始化数组
语法:数据类型 数组名[]={元素值,元素值}
int a[]={2,5,6,7,8,89,90,34,56}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gBVj5fCI-1678672774016)(C:\Users\ADMIN\AppData\Roaming\Typora\typora-user-images\image-20220720145847701.png)]
基本数据类型赋值,这个值就是具体的数据,而且相互不影响。(值拷贝)
int n1 = 2; int n2 = n1;//n2是新开辟的空间,里面的值是具体数值
数组在默认情况下是引用传递,赋的值的是地址。(引用传递)
int arr1[] = {1,2,3};
int arr2[] = arr1;
//系统分配内存,存入数据{1,2,3},并返回地址给arr1
//第二句其实是引用了arr1的地址,如果修改,修改的是同一片内存的值
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NcfjkisJ-1678672774017)(C:\Users\ADMIN\AppData\Roaming\Typora\typora-user-images\image-20220720171318930.png)]
若arr1与arr2指向堆内存内不同两个数据空间,将arr1指向arr2,此时arr1原来的数据空间就没有变量引用,会被当做垃圾,销毁。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wFHRnOnC-1678672774018)(C:\Users\ADMIN\AppData\Roaming\Typora\typora-user-images\image-20220720183833832.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yzifQhCG-1678672774019)(C:\Users\ADMIN\AppData\Roaming\Typora\typora-user-images\image-20220720193442339.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-00dIpy12-1678672774021)(C:\Users\ADMIN\AppData\Roaming\Typora\typora-user-images\image-20220720190156458.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zeXFOj0S-1678672774022)(C:\Users\ADMIN\AppData\Roaming\Typora\typora-user-images\image-20220721095710143.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-l5a3i5Qb-1678672774023)(C:\Users\ADMIN\AppData\Roaming\Typora\typora-user-images\image-20220721101146561.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ns7svcGu-1678672774024)(C:\Users\ADMIN\AppData\Roaming\Typora\typora-user-images\image-20220721101921388.png)]
字符串放在方法区的常量池中,在部分版本中,常量池在堆里(不在方法区里)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VkT9UoTo-1678672774025)(C:\Users\ADMIN\AppData\Roaming\Typora\typora-user-images\image-20220721102738054.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VBNjDOZ7-1678672774026)(C:\Users\ADMIN\AppData\Roaming\Typora\typora-user-images\image-20220721104353676.png)]
声明未创建则cat = null
Person p = new Person();
p.name = "jack";
p.age = 10;
在某些情况下,我们需要定义成员方法(简称方法)。
比如人类:出了有一些属性外(年龄,姓名…),我们人类还有一些行为(说话、跑步、通过学习还能算术)
这时就要用成员方法来完成对类的抽象
public class Method01 {
public static void main(String[] args){
//方法使用
//1.方法写好后,如果不去调用(使用),不会输出
//2.先创建一个对象,然后调用方法即可
Person p1 = new Person();
p1.speak();//调用方法
p1.cal01();
p1.cal02(2);
// 调用getSum方法,同时num1=10,num2=20
// 把方法getSum返回的值,赋给变量returnRes
int returnRes = p1.getSum(10, 5);
System.out.println("getSum返回值:"+returnRes);
}
}
class Person {
String name;
int age;
//方法
//1.public:表示这个方法是公开的
//2.void:表示方法没有返回值
//3.speak():speak是方法名,()是形参列表
//4.{}方法体,可以写我们要执行的代码
public void speak(){
System.out.println("我是一个好人");
}
public void cal01(){
int res = 0;
for(int i = 1;i <= 1000;i++){
res += i;
}
System.out.println("计算结果="+res);
}
// 1.(int n)形参列表,表示当前有一个形参n,可以接收用户输入
public void cal02(int n){
int res=0;
for(int i = 1;i <= n ; i++){
res += i;
}
System.out.println("计算结果="+res);
}
// 1.public表示方法是公开的
// 2.int 表示方法执行后表示返回一个int值
// 3.getSum方法名
// 4.(int num1,int num2)形参列表,两个形参,可以接受用户传入的两个数
// 5.return res;表示把res的值,返回
public int getSum(int num1,int num2){
int res = num1 + num2;
return res;
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lWC3MHyR-1678672774028)(C:\Users\ADMIN\AppData\Roaming\Typora\typora-user-images\image-20220721133328314.png)]
访问修饰符(作用是控制方法的使用范围)
如果不写默认访问,[又四种:public,protected,默认,private],后续讲解
返回数据类型
命名方法:遵循驼峰命名法,最好见名知意
方法调用细节说明:
同一个类中的方法调用:直接调用即可。比如print(参数);
class A{
public void print(int n){
System.out.println("print()被调用 n = "+n);
}
public void sayOK(){
print(10);
System.out.println("执行了sayOK()~~~");
}
}
{
A = a new A();
a.sayOK();
}
------------------------------
print()方法被调用 n = 10
执行了sayOK()~~~
跨类中的方法A类调用B类方法:
需要通过对象名调用。比如:对象名.方法名(参数)
class A{
public void m1(){
System.out.println("m1()开始执行~~~");
//创建B对象,然后调用方法即可
B b = new B();
b.hi();
System.out.println("m1()继续执行~~~");
}
}
class B{
public void hi(){
System.out.println("B类中的hi()被执行")
}
}
{
A = a new A();
a.m1();
}
--------------------------
m1()开始执行~~~
B类中的hi()被执行
m1()继续执行~~~
java中允许同一个类中,多个同名方法的存在,但要求形参列表不一致!
比如:System.out.println();
out是oruntStrean类型
System.out.println(100);
System.out.println("hello,java");
System.out.println(1.1);
System.out.println(true);
java允许将同一个类中多个同名同功能但参数个数不同的方法,封装成一个方法,就可以通过可变参数实现
访问修饰符 返回类型 方法名(数据类型... 形参名){//这三个点注意
}
public class VariableParameter {
public static void main(String[] args){
Method m = new Method();
m.sum(1,2,3,4,5,6);
}
}
class Method{
// 1.int...表示的是可变参数,类型是int,即可以接受多个int(0-多)
// 2.使用可变参数时,可以当作数组来使用,即 parameters 可当作数组
public int sum(int... parameters){
System.out.println("接收参数个数为"+parameters.length);
int res = 0;
for(int i = 0;i < parameters.length; i++){
res += parameters[i];
}
System.out.println(res);
return res;
}
}
注意事项和使用细节
可变参数的实参可以为0个或任意多个
可变参数的实参可以为数组
可变参数的本质就是数组
可变参数可以和普通数字类型的参数一起放在形参列表,但必须保证可变参数在最后
一个形参列表只能出现一个可变参数
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xwRPp30p-1678672774029)(C:\Users\ADMIN\AppData\Roaming\Typora\typora-user-images\image-20220726181818990.png)]
基本使用
面向对象中,变量作用域是非常重要的知识点,相对来说不是特别号理解,要求认真思考,要求深刻掌握变量作用域。
在java编程中,主要的变量就是属性(成员变量)和局部变量。
我们说的局部变量一般是指在成员方法中定义的变量。
java中作用域的分类
全局变量:也就是属性,作用域为整个类体内(如Cat类:cry eat等方法的使用属性)
局部变量:也就是除了属性以外的其它变量,作用域为定义它的代码块中!
全局变量可以默认不赋值,可以直接使用,因为有默认值;
局部变量必须赋值后才能使用,因为没有默认值。
class Cat{
// 全局变量:也就是属性,作用域为整个类体 Cat类:在cry eat等方法使用的属性
int age = 10;
public void say(){
// 1.局部变量一般是指在成员方法中定义的变量,没有默认值。
// n 和 name 就是局部变量
// n 和 name 的作用域在cry方法中
int n = 10;
String name = "jack";
System.out.println("在cry中使用全局属性 age=:" + age);
System.out.println("在cry中使用局部属性 name=:" + name + " n=:" + n);
}
public void eat(){
System.out.println("在eat中使用全局属性 age=:" + age);
// System.out.println("在eat中使用局部属性 name=:" + name); //错误
}
}
注意事项和细节使用
属性和局部变量可以崇明,访问时遵循就近原则。
在同一个作用域中,比如在同一个成员方法中不能重名
属性生命周期较长,伴随着对象的创建而创建,伴随着对象的死亡而死亡。局部变量,生命周期较短,伴随着它的代码块的执行而创建,伴随着代码块的结束而死亡。即在一次方法调用过程中。
作用域范围不同
全局变量/属性:可以被本类使用,或其他类使用(通过在方法内new对象调用,也可以通过参数传入)
局部变量:只能在本类中对应的方法中使用
修饰符不同(protected public private default)
全局变量/属性可以加修饰符
局部变量不可以加修饰符
看一个需求
前面我们在创建人类的对象时,是先把一个对象创建好后,再给他的年龄姓名属性赋值,如果现在我要求,在创建人类的对象时,就直接指定这个对象的年龄和姓名,该怎么做?这是就可以使用构造器。
基本语法
[修饰符] 方法名(形参列表){
方法体
}
基本介绍
构造方法又叫构造器,是类的一种特殊方法,他的主要作用是完成对新对象的初始化。他有几个特点:
public class Constructor01 {
public static void main(String[] args){
Person p1 = new Person("Smith", 80);
System.out.println("p1.name="+p1.name+" p1.age="+p1.age);
}
}
class Person{
String name;
int age;
//1.构造器没有返回值,也不能写void
//2.构造器的名称和类person一样
//3.(String pName,int pAge)是构造器的形参列表,规则和成员方法一样
public Person(String pName,int pAge){
System.out.println("构造器被调用");
name = pName;
age = pAge;
}
}
注意事项和使用细节
一个类可以定义多个不同构造器,即构造器重载(方法重载)
比如:我们可以再给Person类定义一个构造器,用来创建对象的时手,只指定人名,不需要指定年龄
构造器名和类名要相同
构造器没有返回值
构造器是完成对象初始化,并不是创建对象
在创建对象时,系统自动调用该类的构造方法
如果没有定义构造器,系统会自动给类生成一个默认无参构造器(也叫默认构造器),比如Person(){},使用javap指令反编译看看
一旦定义了自己的构造器,默认构造器就被覆盖了,就不能再使用默认的无参数构造器,除非显示定义一下
简介
java虚拟机会给每个对象分配this,代表当前对象。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Pcq5yHNV-1678672774030)(C:\Users\ADMIN\AppData\Roaming\Typora\typora-user-images\image-20220729104142889.png)]
用法
哪个对象调用了方法,方法内this就代表哪个对象
this的注意事项和使用细节
用于调用本类下的另一个构造器,在构造器内使用。
import java.util.Random;
import java.util.Scanner;
public class Homeword14 {
public static void main(String[] args){
int ch;
Scanner myScanner;
GameList gameList = new GameList();
do{
System.out.println("========游戏面板========");
System.out.println(" 1.开始游戏 2.游戏记录 ");
System.out.println(" 3.退出游戏");
myScanner = new Scanner(System.in);
ch = myScanner.nextInt();
switch(ch){
case 1:
while(!(new MoraGame(gameList).gamer.hand == -1)){};
break;
case 2:
gameList.showGameList();
break;
}
}while(ch != 3);
System.out.println("Bye~");
}
}
class GameList{
String gameList[];
public void updateGameList(String result){
if(gameList == null){
gameList = new String[1];
}else{
String newGameList[] = new String[gameList.length+1];
for(int i = 0 ; i < gameList.length ; i++){
newGameList[i] = gameList[i];
}
gameList = newGameList;
}
gameList[gameList.length-1] = result;
}
public void showGameList(){
if(gameList == null){
System.out.println("暂无游戏记录~");
return;
}
System.out.println("局数\t玩家\t电脑\t结局");
for(int i = 0 ; i < gameList.length ; i++){
System.out.println((i+1)+"\t"+gameList[i]);
}
}
}
class MoraGame{
String gameResult;
Gamer gamer = new Gamer();
Robot robot = new Robot();
public MoraGame(GameList gameList){
if(gamer.hand == -1){
return;
}
int result = judge(gamer.hand, robot.hand);
gameResult = translator(gamer.hand) + "\t" + translator(robot.hand) + "\t" + translator(result);
storeGameList(gameList);
System.out.println("========比赛结果========");
System.out.println("玩家\t电脑\t结局");
System.out.println(gameResult);
}
/**
* @param gamerHand 玩家的手势
* @param robotHand 电脑的手势
* @return 输:-1 赢:1 平局:0
*/
public int judge(int gamerHand,int robotHand){
if((gamerHand == 0 && robotHand == 1) || (gamerHand == 1 && robotHand == 2) || (gamerHand == 2 && robotHand == 0)){
return 5;
}else if(gamerHand == robotHand){
return 4;
}else{
return 3;
}
}
public void storeGameList(GameList gameList){
gameList.updateGameList(gameResult);
}
public String translator(int s){
String res = null;
switch(s){
case 0:
res = "石头";
break;
case 1:
res = "剪刀";
break;
case 2:
res = "布";
break;
case 3:
res = "输了";
break;
case 4:
res = "平局";
break;
case 5:
res = "赢了";
break;
}
return res;
}
}
class Gamer{
int hand;
/**
* 构造器
*/
public Gamer(){
hand = getHand();
}
/**
* @return res返回一个0-2的整数 0-石头 1-剪刀 2-布
*/
public int getHand(){
System.out.println("请输入你要出的拳头(0-拳头,1-剪刀,2-布,-1-退出游戏)");
Scanner myScanner = new Scanner(System.in);
int res = myScanner.nextInt();
if(res == -1){
return res;
}
while(!(res < 3 && res >=0)){
System.out.println("数字输入错误");
myScanner = new Scanner(System.in);
res = myScanner.nextInt();
}
return res;
}
}
class Robot{
int hand;
/**
* 构造器
*/
public Robot(){
hand = getHand();
}
/**
* @return res返回一个0-2的整数 0-石头 1-剪刀 2-布
*/
public int getHand(){
Random r1 = new Random();
int res = r1.nextInt(3);
return res;
}
}
常用快捷键
自定义模板
file -> settings -> editor -> live templates ->可以查看/增加模板快捷键
public class TestTemplate {
//main + tab
public static void main(String[] args) {
//sout + tab
System.out.println();
//fori + tab
for (int i = 0; i < 1; i++) {
}
}
}
应用场景(相当于文件夹)
现在有两个程序员共同开发一个java项目,程序员xiaoming希望定义一个类取名Dog,程序员xiaoqiang也想定义一个类叫Dog。两个程序员为此还吵了起来,怎么办?
包的三大作用
包基本语法
package com.xuejava;
包的本质分析(原理)
包的本质 实际上就是创建不同的文件夹/目录来保存类文件
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KRFRM0SF-1678672774031)(C:\Users\ADMIN\AppData\Roaming\Typora\typora-user-images\image-20220730094604528.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cg8iCRRC-1678672774033)(C:\Users\ADMIN\AppData\Roaming\Typora\typora-user-images\image-20220730102103105.png)]
快速入门
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Syp7rkIF-1678672774034)(C:\Users\ADMIN\AppData\Roaming\Typora\typora-user-images\image-20220730103453009.png)]
包的命名
命名规则
只能包含数字、字母、下划线、小圆点,但不能用数字开头,不能是关键字或保留字
命名规范
一般是小写字母+小圆点
com.公司名.项目名.业务模块名
比如:
com.sina.crm.user //用户模块
com.sina.crm.order //订单模块
com.sina.crm.utils //工具模块
常用的包
一个包下,包含很多类,java中常用的包有:
java.lang.* //lang包是基本包,默认引入,不需要再引入
java.util.* //util包,系统提供的工具包,工具类,如Scanner
java.net.* //网络包,网络开发
java.awt.* //是左java的界面开发,GUI
如何引入包
我们引入一个包的主要目的是使用包下面的类,
比如:import java.util.Scanner;就知识引入一个类Scanner
import java.util.*;表示将java.util包中所有类引入
案例:使用系统提供Arrays完成数组排序
package com.hspedu.pkg;
import java.util.Arrays;
public class Import01 {
public static void main(String[] args) {
int arr[] = {-1, 20, 2, 13, 3};
Arrays.sort(arr);
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i] + "\t");
}
}
}
注意事项和使用细节
java提供四种访问控制修饰符号,用于控制方法和属性(成员变量)的访问权限(范围):
封装(encapsulation)就是把抽象出的数据[属性]和对数据的操作[方法]封装在一起,数据被保护在内部,程序的其它部分只有通过被授权的操作[方法],才能对数据进行操作。
举例:对电视机的操作就是典型封装
打开电视,关闭电视在电视内部是一个复杂的过程,但是遥控器可以轻松一个按键进行操作
将属性进行私有化private【不能直接修改属性】
提供一个公共的set方法,用于对属性判断并赋值
public void setXxx(类型 参数名){
//加入数据验证的业务逻辑
属性 = 参数名;
}
提供一个公共的get方法,用于获取属性的值
public XX getXxx(类型 参数名){
//权限判断,Xxx某个属性
return Xxx;
}
package com.hspedu.encap;
public class Encapsulation01 {
//如果要使用快捷键运行,需要先配置主类
//第一次,我们使用鼠标点击形式运行程序,后面就可以用
public static void main(String[] args) {
Person person = new Person();
person.setName("邓圣君");
person.setAge(30);
person.setSalary(30000);
System.out.println(person.info());
//如果我们自己使用构造器指定属性
Person smith = new Person("smith", 2000, 50000);
System.out.println("====smith信息====");
System.out.println(smith.info());
}
}
class Person{
public String name;
private int age;
private double salary;
public Person() {
}
//有三个属性的构造器
public Person(String name, int age, double salary) {
setName(name);
setAge(age);
setSalary(salary);
}
//自己写set和get方法太慢,我们可以使用快捷键(右键->生成->Getter & Setter)
public String getName() {
return name;
}
public void setName(String name) {
if(name.length()>=2&&name.length()<=6){
this.name = name;
}else{
System.out.println("名字长度不对,需要2-6个字符,赋值默认名字");
this.name = "NoName";
}
}
public int getAge() {
return age;
}
public void setAge(int age) {
if (age >=1&&age<=120){
this.age = age;
}else {
System.out.println("年龄范围错误!已赋值默认18");
this.age = 18;
}
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
public String info(){
return "信息为 name="+name+" age="+age+" 薪水="+salary;
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UiEXywAw-1678672774036)(C:\Users\ADMIN\AppData\Roaming\Typora\typora-user-images\image-20220730155747617.png)]
为什么需要继承?
我们编写了两个类,一个是pupil类(小学生),一个是Graduate(研究生)
问题:两个类的属性和方法有很多是相同的,怎么办?
=>答:继承(代码复用)
基本介绍和示意图
继承可以解决代码复用,让我们的编程更加靠近人类思维。当多个类存在相同变量的属性(变量)和方法时,可以从这些类中抽象出父类,在父类中定义这些相同的属性和方法,所有的子类不需要重新定义这些属性和方法,只需要通过extends来声明继承父类即可。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cA0raTwG-1678672774037)(C:\Users\ADMIN\AppData\Roaming\Typora\typora-user-images\image-20220730172138900.png)]
继承的基本语法
class 子类 extends 父类{
}
子类继承了所有属性和方法(包括static),但私有(private)属性和方法在同一包中不能在子类直接访问,要通过公共的方法去访问
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oR1LvzLJ-1678672774038)(C:\Users\ADMIN\AppData\Roaming\Typora\typora-user-images\image-20220730173806854.png)]
子类必定会调用父类的构造器,即super(),完成父类参数的初始化
public class Sub extends Base{
public Sub(){
super();//默认调用父类的无参构造器,不会实例化父类
System.out.println("子类构造器sub()...");
}
public void sayOk(){
//我们发现,父类的非private属性和方法,都可以访问
System.out.println(n1+" "+n2+" "+" "+n3+" "+"n4不能访问"+getN4());
}
}
当创建子类对象时,不管使用子类的哪个构造器,默认情况下总会去调用父类的无参构造器,如果父类没有提供无参构造器,则必须在子类的构造器中用super()
去指定使用父类的哪个构造器完成对父类的初始化工作,否则,编译不会通过。(人话:如果父类不实例化,子类拿不到父类特有的值,所以必须用父类构造器初始化这些值,并不会实例化父类)【实例化详解可以看小知识】
如果希望指定去调用父类的某个构造器,则显式的调用一下
super()在使用时,需要放在构造器第一行
super()和this()都只能放在构造器第一行,因此这俩个方法不能共存在一个构造器。
java所有类都是Object类的子类,Object是所有类的基类。
父类构造器的调用不限于直接父类!将一致往上追溯到Object类(顶级父类)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WR3KoOut-1678672774039)(C:\Users\ADMIN\AppData\Roaming\Typora\typora-user-images\image-20220731103140835.png)]
子类最多只能继承一个父类(即直接继承),即java中是单继承机制。
思考:如何让A类继承B类和C类?
答:让A类继承B类,再让B类继承C类。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Y7inWADG-1678672774041)(C:\Users\ADMIN\AppData\Roaming\Typora\typora-user-images\image-20220731103543324.png)]
不能滥用继承,子类和父类必须满足is-a的逻辑关系
举例:
Person is a Music?错
Cat is a animal?对
基本介绍
super代表父类引用,用于访问父类的属性、方法、构造器
基本语法
访问父类的属性,但不能访问父类的private属性
super.属性名;
访问父类的方法,不能访问父类的private方法
super.方法名(参数列表);
访问父类的构造器:
super(参数列表);只能放在构造器的第一句,只能出现一句!
细节
案例
我们看一个案例来分析当子类继承父类,创建子类对象时,内存中到底发生了什么?
重点:当子类对象创建好后,建立了怎样的查找关系
PS:如果父类有这个属性,但不能访问(private),则报错,并不会再向上查找。(但可以通过父类的公共方法去访问)
虽然说是向上查找的机制,但可以简单概括为覆盖的意思
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-36E4Dtp5-1678672774042)(C:\Users\ADMIN\AppData\Roaming\Typora\typora-user-images\image-20220731121231727.png)]
步骤:
基本介绍
简单的说:方法覆盖(重写)就是子类有一个方法,和父类的某个方法名称、返回类型、参数一致,那么我们就是说子类的这个方法覆盖了父类的那个方法。
细节
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TzF2aBAj-1678672774043)(C:\Users\ADMIN\AppData\Roaming\Typora\typora-user-images\image-20220802100817670.png)]
先看一个问题
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Q0PyRme9-1678672774045)(C:\Users\ADMIN\AppData\Roaming\Typora\typora-user-images\image-20220802111939194.png)]
基本介绍
方法或对象具有多种形态。是面向对象的第三大特征,多态是建立在封装和继承基础之上的。
方法的多态:重写和重载就体现多态
//方法重载体现多态
A a = new A();
//我们传入不同参数就会调用不同sum方法
System.out.println(a.sum(1,2));
System.out.println(a.sum(1,3,2));
//方法重写体现多态
B b = new B();
a.say();
b.say();
对象的多态【重点】
(1)一个对象的编译类型和运行类型可以不一致
(2)编译类型在定义对象时,就确定了,不能改变
(3)运行类型是可以变化的
(4)编译类型看定义时 ‘=’ 号的左边,运行类型看 ‘=’ 号的右边
Animal animal = new Dog();//[animal编译类型是Animal,运行类型是Dog]
animal = new Cat();//[animal的运行类型变成了Cat,编译类型仍然是Animal]
//Cat is Animal思想
即父类可引用子类对象
编译类型是约束,运行类型是行为,行为必须遵循约束
//主人给小狗喂食骨头
public void feed(Dog dog,Bone bone){
System.out.println("主人 "+name+" 给 "+dog.getName()+" 吃 "+bone.getName());
}
public void feed(Cat cat,Fish fish){
System.out.println("主人 "+name+" 给 "+cat.getName()+" 吃 "+fish.getName());
}
//如果动物很多,食物很多
//---》feed方法很多,不利于管理和维护
//pig --》 rice
// tiger --》meat...
//使用多态思想
public void feed(Animal animal,Food food){
System.out.println("主人 "+name+" 给 "+animal.getName()+" 吃 "+food.getName());
}
//animal 编译类型Animal,可以指向Animal子类对象
//food 编译类型是Food,可以指向Food子类对象
多态的前提是:两个对象(类)存在继承关系
多态的向上转型
本质:父类的引用指向了子类的对象(对象不会变,变的是地址),但是编译器认为成员只有父类的成员,运行遵循继承规则
[有点类似于基础篇的数据类型的转换]
特点:
Animal animal = new Cat();
//可以调用父类中所有成员(需遵循访问权限)
//但是不能调用子类的特有成员
//因为在编译阶段,能调用哪些成员,是由编译类型决定的
animal.catchMouse();//错误
//遵守访问权限
animal.show();//错误
//最终运行效果看子类的具体实现
animal.eat();
//输出:猫吃鱼,而不是 吃.
Animal animal = new Cat();
//可以调用父类中所有成员(需遵循访问权限)
//但是不能调用子类的特有成员
//因为在编译阶段,能调用哪些成员,是由编译类型决定的
Cat cat = (Cat)animal;
cat.catchMouse();
属性没有重写之说!属性的值看编译类型
package com.hspedu.poly_.detail;
public class PolyDetail02 {
public static void main(String[] args) {
Base base = new Sub();
System.out.println(base.count);//输出10
}
}
class Base{
int count = 10;
}
class Sub extends Base{
int count = 20;
}
instanceOf比较符,用于判断对象的类型是否为XX类型或XX类型的子类
子类型对象 instanceof 父类/本类 结果为true;
父类型对象 instanceof 子类 结果为false;
[即true表示 对象 是 指定类型的子类或者本类 , false表示 对象 是 指定类型的父类 或者 不属于该类型]
[如果instanceof为true,则 该对象 必定能使用Student类方法]多态数组详解
package com.hspedu.poly_.detail;
public class PolyDetail03 {
public static void main(String[] args) {
BB bb = new BB();
System.out.println(bb instanceof BB);// true
System.out.println(bb instanceof AA);// true
AA aa = new BB();
System.out.println(aa instanceof AA);// true
System.out.println(aa instanceof BB);// true
}
}
class AA{}//父类
class BB extends AA{}//子类
package com.hspedu.poly_.dynamic_;
public class DynamicBinding {
public static void main(String[] args) {
A a = new B();
System.out.println(a.sum());
System.out.println(a.sum1());
}
}
class B extends A{
public int i = 20;
// public int sum(){
// return i+20;
// }
public int sum1(){
return i+10;
}
public int getI(){
return i+10;
}
}
class A {
public int i = 10;
public int sum(){
return getI()+10;
}
public int sum1(){
return i+10;
}
public int getI(){
return i;
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1fB6TsOQ-1678672774047)(C:\Users\ADMIN\AppData\Roaming\Typora\typora-user-images\image-20220802195836017.png)]
public class PolyArray {
public static void main(String[] args) {
Person person[] = new Person[5];
person[0] = new Person("jack",20);
person[1] = new Student("jack",18,100);
person[2] = new Student("smith",19,30.1);
person[3] = new Teacher("scott",30,20000);
person[4] = new Teacher("king",50,25000);
for (int i = 0; i < person.length; i++) {
//提示:person[i]编译类型是Person,运行类型是根据实际情况由JVM来判断
System.out.println(person[i].say());//动态绑定机制
}
}
}
public class Person {
private String name;
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;
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
private int age;
public String say() {
return name + " " + age;
}
}
public class Student extends Person {
private double score;
public Student(String name, int age, double score) {
super(name, age);
this.score = score;
}
public double getScore() {
return score;
}
public void setScore(double score) {
this.score = score;
}
public String say() {
return super.say() + " " + score;
}
}
public class Teacher extends Person {
private double salary;
public Teacher(String name, int age, double salary) {
super(name, age);
this.salary = salary;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
public String say() {
return super.say() + " " + salary;
}
}
public class PolyArray {
public static void main(String[] args) {
Person person[] = new Person[5];
person[0] = new Person("jack",20);
person[1] = new Student("jack",18,100);
person[2] = new Student("smith",19,30.1);
person[3] = new Teacher("scott",30,20000);
person[4] = new Teacher("king",50,25000);
for (int i = 0; i < person.length; i++) {
//提示:person[i]编译类型是Person,运行类型是根据实际情况由JVM来判断
System.out.println(person[i].say());//动态绑定机制
//这里大家就要动脑筋。如果instanceof为true,则 该对象 必定能使用Student类方法
if(person[i] instanceof Student){
Student student = (Student)person[i];
student.study();
} else if (person[i] instanceof Teacher) {
Teacher teacher = (Teacher)person[i];
teacher.teach();
}else {
System.out.println("你的类型有误,请自己检查");
}
}
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SlWrlXsG-1678672774048)(C:\Users\ADMIN\AppData\Roaming\Typora\typora-user-images\image-20220803100129272.png)]
==
(1)==:如果判断基本类型,判断的是值是否相等.
(2)==:如果判断的是引用类型,判断的是地址是否相等,即判定是不是同一个对象
equals源码查看
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = value.length;
if (n == anotherString.value.length) {
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}
equals默认判断的是地址是否相等,子类中往往重写该方法,用于判断内容是否相等.
比如Integer,String
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sA2H1WFc-1678672774049)(C:\Users\ADMIN\AppData\Roaming\Typora\typora-user-images\image-20220803114657503.png)]
重写equals方法
基本介绍
默认返回 : 全类名(包名+类名) + @ + 哈希值的十六进制
子类往往重写toString方法,用于返回对象的属性信息
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bwhTg3pq-1678672774049)(C:\Users\ADMIN\AppData\Roaming\Typora\typora-user-images\image-20220803134459025.png)]
重写toString方法,打印对或拼接对象时,都会自动调用该对象的toString形式
演示:
public String toString() {
return "Monster{" +
"name='" + name + '\'' +
", job='" + job + '\'' +
", sal=" + sal +
'}';
}
当直接输出一个对象时,toString方法会被默认调用.
一个需求
断点调试介绍
断点调试的快捷键
idea如何进入jdk原码?
使用强迫进入
实现基于文本界面的《房屋出租软件》
能够实现对房屋信息的添加、修改和删除(用数组实现),并能够打印房屋明细表
主菜单
新增房源
查找房源
删除房源
修改房源
分层模式=>当软件比较复杂,需要管理模式(mcv–model control view)
准备工具类Utility,提高开发效率
在实际开发中,公司都会提高相应的工具类和开发库,可以提高开发效率,程序员也需要能够看懂别人写的代码,并能够正确调用
编号 房主 电话 地址 月租 状态(未出租/已出租)
package com.hspedu.houserent.domain;
public class House {
private int id;//编号
private String name;//房主
private String phone;//电话
private String address;//地址
private double rent;//月租
private String state;//状态(未出租/已出租)
//构造器和setter,getter
public House(int id, String name, String phone, String address, double rent, String state) {
this.id = id;
this.name = name;
this.phone = phone;
this.address = address;
this.rent = rent;
this.state = state;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public double getRent() {
return rent;
}
public void setRent(double rent) {
this.rent = rent;
}
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
//为了方便我们查看对象,重写toString
@Override
public String toString() {
return "House{" +
"id=" + id +
", name='" + name + '\'' +
", phone='" + phone + '\'' +
", address='" + address + '\'' +
", rent=" + rent +
", state='" + state + '\'' +
'}';
}
}
化繁为简:一个一个功能实现
说明:实现功能的三部曲[明确功能->思路分析->代码实现]
功能说明:
用户打开软件,可以看到主菜单,可以退出软件。
思路分析:
在HourseView.java中,编写一个方法mainMenu,显示菜单
代码实现:
package com.hspedu.houserent.view;
import com.hspedu.houserent.utils.Utility;
/*
* 1.显示界面
* 2.接收用户的输入
* 3.调用HouseService完成对房屋信息的各种操作
* */
public class HouseView {
private boolean loop = true;//控制显示菜单循环
private char key = ' ';//接收用户选择
// 显示主菜单
public void mainMenu(){
do{
System.out.println("===============房屋出租系统菜单===============");
System.out.println("\t\t\t1.新 增 房 屋");
System.out.println("\t\t\t2.查 找 房 屋");
System.out.println("\t\t\t3.删 除 房 屋 信 息");
System.out.println("\t\t\t4.修 改 房 屋 信 息");
System.out.println("\t\t\t5.房 屋 列 表");
System.out.println("\t\t\t6.退出");
System.out.print("请输入你的选择(1-6):");
key = Utility.readChar();
switch (key){
case '1':
System.out.println("新 增 房 屋");
break;
case '2':
System.out.println("查 找 房 屋");
break;
case '3':
System.out.println("删 除 房 屋 信 息");
break;
case '4':
System.out.println("修 改 房 屋 信 息");
break;
case '5':
System.out.println("房 屋 列 表");
break;
case '6':
System.out.println("退 出");
loop = false;
break;
}
}while(loop);
}
}
功能说明:
思路分析:
需要编写HouseView.java和HouseService.java
代码实现:
功能说明:
思路分析:
代码实现:
规定:新添加的房屋id按照自增长来
目录
没有静态类的说法
提出问题
有一群小孩再堆雪人,不时有新的小孩加入,请问如何知道现在共有多少人在玩?编写程序解决
快速入门
思考:
如果设计一个int count 表示总人数,我们再创建一个小孩时,就把count加1,并且count是所有对象共享的就ok了!
类变量内存布局
可以肯定的是,类变量是被对象共享的,因此类变量其实是引用类型,地址被引用了
Java static变量保存在哪?CSDN博客
java中的静态变量和Class对象究竟存放在哪个区域? - 知乎 (zhihu.com)
共识:
注意事项
什么时候需要用类变量(使用场景)
当我们需要让某个类的所有对象都共享一个变量时,就可以考虑使用类变量(静态变量):比如:定义学生类,统计所有学生共交多少钱。Student(name,fee)
类变量与实例变量(普通属性)区别
类变量是该类的所有对象共享的,而实例变量是每个对象独享的。
加上static称为类变量或静态类型,否则称为实例变量/普通变量/非静态变量
类变量可以通过 类名.类变量名 或者 对象名.类变量名 来访问,但java设计者推荐我们使用 类名.类变量名 方式访问。【前提是满足修饰符的访问权限和访问】
实例变量不能通过 类名.类变量名 方式访问
类变量是在类加载时就初始化了,也就是说,即使你没有创建对象,只要加载类了,就可以使用类变量了。
类变量的生命周期是随类的家宅开始,随类的消亡而销毁
类方法景点的使用场景
当方法中不涉及到任何和对象相关的成员,则可以将方法涉及成静态方法,提高开发效率
比如:工具类中的方法utils,Math类,Arrays类,Collections集合类
小结
在程序实际开发中,往往会将一些通用的方法,设计成静态方法,这样我们不需要创建对象就可以使用了,比如打印一维数组,冒泡排序,完成某个计算任务等…
注意细节
类方法和普通方法都是随着类的加载而加载,将构造信息存储在方法区
⭐类方法中无this的参数
⭐普通方法中隐含着this参数
这个细节使得静态方法的使用场景比较独特
类方法可以通过类名调用,也可以通过对象名调用
普通方法和对象有关,需要通过对象名调用,比如对象名.方法名(参数),不能通过类名调用
深入理解main方法
解释main方法的形式:public static void main(String[] args){}
java虚拟机需要调用类的main()方法,所以该方法的访问权限必须是public
java虚拟机在执行main()方法时不必创建对象,所以该方法必须是static
该方法接收String类型的数组参数,该数组中保存执行java命令时传递给所运行的类的参数
java 执行的程序 参数1 参数2 参数3
特别提示
如何在idea中传递参数
基本介绍
代码化块又称为初始化块,属于类中的成员[即 是类的一部分],类似于方法,将逻辑语句封装在方法体中,通过{}包围起来
但和方法不同,没有方法名,没有返回,没有参数,只有方法体,而且不用勇敢对象或者类显式调用,而是加载类时或创建对象时,隐式调用
基本语法
[修饰符]{
代码
};
注意:
解说
细节
static代码块也叫静态代码块,作用就是对类进行初始化,而且它随着类的加载而执行,并且只会执行一次。
如果是普通代码块,每创建一个对象,就执行。
类声明时候被加载?⭐
普通的代码块,在创建对象实例时,会被隐式的调用。
被创建一次,就会调用一次
如果只是使用类的静态成员时,普通代码块并不会执行。
创建一个对象时,在一个类调用顺序是:⭐
构造器的最前面其实隐含了super()和调用普通代码块
静态相关的代码块,属性初始化,在类加载时就执行完毕,因此是优先于构造器的普通代码块执行的
我们看一下创建一个子类对象时(继承关系),他们的静态代码块,静态属性初始化,普通代码块,普通属性初始化,构造方法的调用顺序如下:⭐
静态代码块只能直接调用静态成员(静态属性和静态方法),普通代码块可以调用任意成员
演示饿汉式和懒汉式单例模式的实现
步骤如下:
构造器私有化(防止直接new一个新对象)
类的内部创建对象
向外暴露一个静态的公共方法。getInstance()
代码实现:(饿汉式)-只要类被加载,对象就生成了(急不急啊,急死你得了)【还没用到这个对象,就已经生成了对象】
public class SingleTon01 {
public static void main(String[] args) {
GirlFriend gf = GirlFriend.getInstance();
}
}
//有一个类,GirlFriend
//只能有一个女朋友
class GirlFriend{
private String name;
//如何保证我们只能创建一个 girlFriend对象
//步骤【单例模式-饿汉式】
//1.将构造器私有化
private GirlFriend(String name) {
this.name = name;
}
//2.在类的内部直接创建静态对象
private static GirlFriend gf = new GirlFriend("小红");
//3.提供一个公共static方法 返回gf对象(静态方法只能调用静态成员)
public static GirlFriend getInstance(){
return gf;
}
//总结:通过类的加载只会加载一次的特性,当静态方法被调用,类对象加载就只会开辟唯一一个空间,
//生成唯一一个对象,并返回。
}
代码实现:(懒汉式)-使用的时候才会创建实例
package com.hspedu.single_;
import com.sun.org.apache.bcel.internal.generic.IF_ACMPEQ;
/*
* 演示懒汉式的单例模式
* */
public static void main(String[] args) {
Cat cat = Cat.getInstance();
}
//希望程序运行过程中只能养一只cat
class Cat{
private String name;
//步骤
//1.仍然把构造器私有化(防止在外部再new一个对象)
private Cat(String name) {
this.name = name;
}
//2.定义一个static静态属性对象(区别于饿汉式)
private static Cat cat;
//3.提供一个public的static,可以返回一个cat对象(区别于饿汉式),在调用方法的时候才生成对象,
// 即使类被加载,也不会立即生成对象
public static Cat getInstance(){
if(cat == null){
cat = new Cat("小咪");
}
return cat;
}
//总结:通过类的加载只会加载一次的特性,当静态方法被调用,类对象加载就只会开辟唯一一个空间,
//生成唯一一个对象,并返回。
//于饿汉式区别:
//3.提供一个public的static,可以返回一个cat对象(区别于饿汉式),在调用方法的时候才生成对象,
// 即使类被加载,也不会立即生成对象
}
懒汉式VS饿汉式
二者最主要的区别在于创建对象的时机不同:饿汉式是在类加载就创建了对象实例,而懒汉式是在使用时才创建
饿汉式不存在线程安全,懒汉式存在线程安全问题(后面学习线程后完善)
同时执行存在创建3个对象的情况
**饿汉式存在浪费资源的可能。**因为如果程序员一个对象实例都没有使用,那么饿汉式创建的对象就浪费了,懒汉hi是使用时才创建,就不存在这个问题。
在外面javaSE标准类中,java.lang.Runtime就是经典的单例模式-饿汉式
final可以修饰类、属性、方法和局部变量。
在某些情况下,程序员可能有以下需求,就会使用到final:
食用细节
final修饰的属性一般又叫常量,一般用 XX_XX_XX来命名 如:税率 TAX_RATE
final修饰的属性在定义时,必须赋初值,并且以后不能再修改,复制可以再如下位置【选择一个位置赋初值即可】:
如果final修饰的属性是静态的,则初始化的位置只能是
不能在构造器中赋值,因为类加载的时候就需要初始化了,不能等到实例后才初始化。
final类不能继承,但是可以实例化对象。
如果类不是final类,但是含有final方法,则该方法虽然不能重写但是可以被继承
一般来说,如果一个类已经是final类了,就没有必要再将方法修饰成final方法。(不能继承自然也不能重写里面的方法了)
final不能修饰构造方法(即构造器)
final和static往往搭配使用,效率更高,底层编译器做了优化处理**(即,不会导致类加载!!!⭐)**
包装类(Integer,Double,Float,Boolean等都是final),String也是final类
提出问题
一个小问题,看个程序
父类方法的不确定性:这是个动物,但是动物分荤食和素食,里面又分很多种食物,eat()具有不确定性
小结:当父类的某些方法,需要声明,但是有不确定如何实现时,可以将其声明为抽象方法,那么这个类就是抽象类
简介
当一个父类的一些方法不能确定时,可以用abstract关键字来修司该方法,这个方法就是抽象方法,用abstract来修饰该类就是抽象类
用abstract关键字来修饰一个类时,这个类就叫抽象类
访问修饰符 abstract 类名{
}
用abstract关键字来修饰一个方法时,这个方法就是抽象方法
访问修饰符 abstract 返回类型 方法名(参数列表);//没有方法体
抽象类的价值更多作用是在于设计,是设计者设计好后,让子类继承并实现抽象类
抽象类,是考官比较爱问的知识点,在框架和设计模式使用比较多
细节
抽象类不能被实例化
抽象类不一定要包含abstract方法。也就是说,抽象类可以没有abstract方法
一旦类包含了abstract方法,则这个类必须声明为abstract
abstract只能修饰类和方法,不能修饰属性和其它的。
抽象类可以拥有任意成员【抽象类本质还是类】,比如:非抽象方法,构造器,静态属性等等…
抽象方法不能有主体(代码块)
如果一个类继承了抽象类,则它必须实现抽象类的所有抽象方法,触发它自己也声明为abstract类
抽象方法不能使用private、final和static来修饰,因为这些关键字都是和重写相违背的(且自带public)
private:重写不能提高访问权限
final:无法继承或重写
static:静态仅在类加载时执行,无法重写或继承
案例
我们看看如何把Animal做成抽象类,并让子类Cat类实现
实践-模板设计模式
需求:
实践:
设计一个抽象类(Template),能完成如下功能:
为什么有接口?(场景)
usb插槽就是显示生活中的接口
你可以把手机、相机、u盘都插在usb插槽上,而不用担心哪个插槽是专门插哪个,原因是做usb插槽的厂家和做各种设备的厂家都遵守了统一的规定包括尺寸,排线等等…
快速入门
这样的设计需求在java编程/php/.net/go中也是会大量存在的,我曾经说过,一个程序就是一个世界,在显示世界存在的情况,在程序中也会出现。我们用程序来模拟一下。
接口:规定需要有某个功能,但是功能的具体实现交给厂家(USER)实现
接口就是给出一些没有实现的方法,封装到一起,到某个类要使用的时候,在根据具体情况把这些方法写出来。语法:
interface 接口名{
//属性
//方法,在接口中,可以省略abstract关键字
}
class 类名 implements 接口{
//自己的属性
//自己的方法
//必须实现的接口的抽象方法
}
小结:
对初学者来讲,理解接口的概念不算太难,难的是不知道声明时候使用接口,下面我例举几个应用场景:
说现在要制造战斗机,武装直升机。专家只需把飞机需要的功能/规格定下来即可,然后让别的人具体实现即可。
现在有一个项目经理,管理三个程序员,开发一个软件,为了控制和管理软件,项目经理可以定义一些接口,然后由程序员具体实现。
实际要求:三个程序员,编写三个类,分别完成对MYSQL,Oracle,DB2数据库的连接connect,close…
接口不能被实例化
⭐接口中方法默认修饰符:public abstract,接口中抽象方法,可以不用abstract修饰符和public修饰符 图示:
一个普通类实现接口,就必须将该接口的所有方法都实现,可以使用alt+enter或alt+i来实现重写
抽象类implements接口,可以不用实现接口的方法
一个类可以同时实现多个接口【A extends B,A就不能再去继承C类了,但是接口可以多实现(implements),因此不能将接口和抽象类一样简单看成是类的另一种形式】
接口中的属性,只能是final的,而且是public static final修饰符。比如:int a=1;实际上是public static final int a=1;(因此必须初始化)
接口中属性的访问形式:接口名.属性名[不是简单的继承,是多实现的基础]
⭐接口不能继承其它的类,但是可以继承多个别的接口【接口可以继承接口】
interface A extends B,C{}
接口的修饰符只能是public和默认,这点和类的修饰符是一样的
小猴子继承老猴子,但是游泳和飞翔,小猴并没有鸟的翅膀和鱼的筛,小猴学会飞翔和游泳需要自己的方式去实现。
接口和继承的解决问题不同
继承的价值主要在于:解决代码的复用性和可维护性
接口的价值主要在于:设计,设计好各种规范(方法),让其它类去实现这些方法。即更加灵活
接口比继承更加灵活【单继承,多实现】
接口比继承更加灵活,继承是满足is-a关系,而接口只需满足like-a关系
接口再一定成都上实现代码解耦[即:接口规范性+动态绑定]
多态参数(前面案例体现)
再前面USB接口案例,USB usb,既可以接收手机对象,又可以接收相机对象,就体现了接口的多态(接口引用可以指向实现了接口的类的对象)
多态数组
演示一个案例:给USB数组中,存放Phone 和 Camera对象,Phone类还有一个特有方法call(),遍历数组,如果是Phone对象,除了调用USB接口定义的方法外,还需要调用Phone特有方法call()
接口存在多态传递现象(即C继承B后也实现了接口AInterface1)【接口也能继承】
interface A{
int x = 0;//public static int x = 0;
}
class B{
int x = 1;
}
interface C{
int x = 2;//public static int x = 0;
}
public class D extends B implements A,C{
public static void main(String[] args){
System.out.println(A.x + super.x + C.x);//不能直接用x输出,会报错
}
}
一个类的内部又完整的嵌套了另一个类结构。被嵌套的累被称为内部类(inner class),嵌套其它类的类称为外部类(outer calss)。是我们类的第五大成员【思考:类的五大成员是哪些?[属性、方法、构造器、代码块、内部类]】,内部类最大的特点就是可以直接访问私有属性,并且可以体现类与类之间的包含关系。
注意:内部类是学习的难点,同时也是重点,后面看底层原码时,有大量的内部类
基本语法:
class Outer{//外部类
class Inner{//内部类
}
}
class Other{//外部其它类
}
说明:局部内部类是定义在外部类的局部位置,比如方法中,并且有类名。
说明:匿名内部类是定义再外部类的局部变量,比如:方法、代码块中,并且没用类名
基本语法:
new 类或接口(参数列表){
//类体
};
需求:想使用一个接口并创建对象,但是这个对象的类只使用了一次,后面不再使用
传统方式方式:写一个类,实现接口,并创建对象
匿名内部类方式的方式简化了传统方式的书写。
在外部类的命名基础上,增加了$1的编号,如:
class Outer{
Outer o1 = new Outer(){}
}
//上面的对象的类名字为Outer$1
Father f1 = new Father("jack"){
//类体
};//@情况1
Father f1 = new Father("jack");//@情况2
情况1的匿名内部类的运行类型是 Father$1,而情况2的运行类型则是Father
两者的区别仅是有无类体。
原因:
情况1有类体,说明你在父类Father的基础上做了修改(方法的重写等…),而情况2没有类体则说明没有做修改(说明只是简单的继承),所以两者运行类型有所不同。
实际上是一个立即创建对象过程的类,有点类似与单调模式的饿汉式,该类只使用一次,执行则立即创建唯一对象,其继承关系由参数括号左边的名字决定继承与实现。(因为其匿名特性,无法重写构造器)
new Person(){
public int n1 = 10;
}.n1=20;
细节
⭐匿名内部类的最佳实践
当作实参直接传递,简洁高效【个人认为:有点像js的回调函数(即,传入一个方法作为实参,在另外的方法调用该参数(方法),可以灵活变化),java更复杂,需要创建一个匿名内部类以及对应接口达到方法的传入的目的】
interface AA{
public void cry();
}
main类中://形参是接口类型AA
public static void show(AA a){
a.cry();
}
main方法中:
show(new AA(){
public void cry(){
System.out.println("AA cry");
}
});
说明:成员内部类是定义在外部类的成员位置,并且没用static修饰【局部内部类、匿名内部类在方法、代码块、形参中使用】
可以直接访问外部类的所有成员,包含私有的
可以添加任意访问修饰符(public、protected、默认、private)因为它的低位就是一个成员
作用域
和外部类的其它成员一样,为整个类体比如前面案例,在外部类的成员方法中创建成员内部类对象,再调用方法。
成员内部类访问外部类(比如属性)【访问方式:直接访问】
外部类访问内部类【范围方式:创建对象】
其他外部类访问成员内部类
new了外部类再去new成员内部类,或通过方法返回一个对象
如果外部类和内部类的成员重名时,内部类访问的化,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类名.this.成员)去访问
说明:静态内部类是定义在外部类的成员位置,可参考类变量知识(可通过外部类名直接访问,不需要new外部类)
可以直接访问外部类的所有静态成员,包括私有的,但不能直接访问非静态成员
可以添加任意访问修饰符(public、protected、default、private)因为它的低位就是一个成员
作用域:同其它的成员,为一个整体的类体
静态内部类-访问-外部类(比如:静态属性)【访问方式:直接访问所有静态成员】
外部类-访问-静态内部类【访问方式:创建对象,再访问】
外部其它类-访问-静态内部类【访问方式:①可通过外部类名直接访问,不需要new外部类,但是不可以通过对象名来调用。②编写一个方法,可以返回静态内部类的对象实例】
如果外部类和静态内部类的成员重名时,静态内部类访问,遵循就近原则,如果访问外部类的成员,则可以使用(外部类名.成员)去访问【与成员内部类差别是不使用this,因为可以直接访问的也只能是静态成员】
先看需求
要求创建季节(Season)对象,请设计完成。
class Season{
private String name;
private String desc;//描述
//构造器
//getter&&setter
}
//因为对于季节而已,它的对象(具体值),是固定的四个,不会有更多
//按照传统设计类的思路,不能体现季节是有限的四个对象
//因此,这样的设计不好====》枚举类
解决方案-枚举
演示
final class Season{
private String name;
private String desc;//描述
//定义了四个对象
public static final Season SPRING = new Season("春天", "温暖");
public static final Season SUMMER = new Season("夏天", "炎热");
public static final Season AUTUMN = new Season("秋天", "凉爽");
public static final Season WINTER = new Season("冬天", "寒冷");
//1.将构造器私有化private,目的:防止直接new
//2.去掉setter相关方法:防止属性被修改
//3.在Season内部,直接创建固定的对象
//4.优化,对象可以加入final修饰符
private Season(String name, String desc) {
this.name = name;
this.desc = desc;
}
//2.去掉setter相关方法:防止属性被修改
public String getName() {
return name;
}
public String getDesc() {
return desc;
}
@Override
public String toString() {
return "Season{" +
"name='" + name + '\'' +
", desc='" + desc + '\'' +
'}';
}
}
规则
演示
//如果使用了enum来实现枚举类
//1.使用关键字enum替代class
//2.public static final Season SPRING = new Season("春天","温暖") 用 SPRING("春天", "温暖") 替代
// SPRING("春天", "温暖")解读 常量名(实参列表)
//3.⭐⭐枚举属性必须前置⭐⭐
//4.如果有多个常量(对象),使用,号间隔即可
enum Season2{
//定义了四个对象
SPRING("春天", "温暖"),SUMMER("夏天", "炎热"),AUTUMN("秋天", "凉爽"),WINTER("冬天", "寒冷");
private String name;
private String desc;//描述
//1.将构造器私有化private,目的:防止直接new
//2.去掉setter相关方法:防止属性被修改
//3.在Season内部,直接创建固定的对象
private Season2(String name, String desc) {
this.name = name;
this.desc = desc;
}
//2.去掉setter相关方法:防止属性被修改
public String getName() {
return name;
}
public String getDesc() {
return desc;
}
@Override
public String toString() {
return "Season{" +
"name='" + name + '\'' +
", desc='" + desc + '\'' +
'}';
}
}
注意事项
当我们使用enum关键字开发一个枚举类时候,默认会继承Enum类
传统public static final Season SPRING = new Season(“春天”,“温暖”) 用 SPRING(“春天”, “温暖”) 替代
如果使用无参构造器创建枚举对象,则实参列表和小括号可以省略
当有多个枚举对象时,使用,间隔,最后有一个分号结尾
枚举对象必须放在枚举类的行首
SPRING(“春天”, “温暖”),实际上是调用构造器,其需要创建对应的构造器
如果我们使用的是无参构造器创建常量对象,则可以省略括号,如下图what
我们称上图的成员为枚举对象
values:返回枚举对象数组
Color green = Color.GREEN;
switch (green){
case RED:
System.out.println("匹配到红色");
break;
default:
System.out.println("others");
}
switch()中,放入枚举对象
在每个case后,直接写上在枚举类中,定义的枚举对象即可。
使用enum关键字后,就不能再继承其它类了,因为enum会隐式继承Enum,而java是单继承机制
枚举类和普通类一样,可以实现接口,如下形式。
enum 类名 implements 接口1,接口2{
}
使用Annotation时要在其面前增加@符号,并把该Annotation当成一个修饰符使用。用于修饰它支持的程序元素。
三个基本的Annotation
@Override
如果写了@Override注解,编译器就会去检查该方法是否真的重写了父类的方法,如果的确重写了,则编译通过,如果没用构成重写,则编译错误。@Override其实本质上是一个注解类。
@Deprecated
@SuppressWarnings
当我们不希望在代码上看到黄色警告信息的时候,可以用SuppressWarnings注解来抑制警告信息
在{“”}中,可以写入你希望抑制(不显示)警告信息
关于SupressWarnings作用访问是和你放置的位置相关
比如@SuppressWarnings放置在main方法,那么抑制警告的访问就在main方法,如上图。
看看@SuppressWarnings源码
放置的位置就是TYPE,FIELD,METHOD,PARAMETER,CONSTRUCTOR,LOCAL_VARIBLE
该注解类有数组String[] values() 设置一个数组比如{“rawtypes”,“unchecked”}
可以指定的警告类型有如下:(用于排查bug用处极大)
关键字 | 用途 |
---|---|
all | 抑制所有警告 |
boxing | to suppress warnings relative to boxing/unboxing operations/抑制装箱、拆箱操作时候的警告 |
cast | to suppress warnings relative to cast operations/抑制映射相关的警告 |
dep-ann | to suppress warnings relative to deprecated annotation/抑制启用注释的警告 |
deprecation | to suppress warnings relative to deprecation/抑制过期方法警告 |
fallthrough | to suppress warnings relative to missing breaks in switch statements/抑制确在switch中缺失breaks的警告 |
finally | to suppress warnings relative to finally block that don’t return/抑制finally模块没有返回的警告 |
hiding | to suppress warnings relative to locals that hide variable |
incomplete-switch | to suppress warnings relative to missing entries in a switch statement (enum case)/忽略没有完整的switch语句 |
nls | to suppress warnings relative to non-nls string literals/忽略非nls格式的字符 |
null | to suppress warnings relative to null analysis/忽略对null的操作 |
rawtypes | to suppress warnings relative to un-specific types when using generics on class params/使用generics时忽略没有指定相应的类型 |
restriction | to suppress warnings relative to usage of discouraged or forbidden references |
serial | to suppress warnings relative to missing serialVersionUID field for a serializable class/忽略在serializable类中没有声明serialVersionUID变量 |
static-access | to suppress warnings relative to incorrect static access/抑制不正确的静态访问方式警告 |
synthetic-access | to suppress warnings relative to unoptimized access from inner classes/抑制子类没有按最优方法访问内部类的警告 |
unchecked | to suppress warnings relative to unchecked operations/抑制没有进行类型检查操作的警告 |
unqualified-field-access | to suppress warnings relative to field access unqualified/抑制没有权限访问的域的警告 |
unused | to suppress warnings relative to unused code/抑制没被使用过的代码的警告 |
补充说明:@interface的说明
元注解基本介绍
JDK的元Annotation用于修饰其它Annotation
元注解:本身作用不大,讲这个原因希望同学们,看源码时,可以知道他是干什么的
元注解种类(使用不多,了解,不用深入研究,想深入可以看源码)
Retention //指定注解的作用范围,三种SOURCE,CLASS,RUNTIME
Target //指定注解可以在哪些地方使用
Documented //指定该注解是否会在javadoc体现
Inherited //子类会继承父类注解
所谓异常就是程序运行时可能出现的一些错误,比如试图打开一个根本不存在的文件灯,异常处理将会改变程序中的控制流程,让程序有机会对程序做处理。
场景
解决
如果程序员认为一段代码可能出现异常/问题,可以使用try-catch异常处理机制来解决
从而保证程序的健壮性
将代码块->选中->快捷键ctrl+alt+t ->选中try-catch
如果进行异常处理,那么即使程序异常,依然可以运行
try{
int res = num1/num2;
}catch(Exception e){
e.printStackTrace();
System.out.println("出现异常的原因="+e.getMessage());
}
System.out.println("程序继续运行");
基本概念
Java语言中,将程序执行中发生的不正常情况称为”异常“。(开发过程中的语法错误和逻辑错误不是异常)
执行过程中所发生的异常事件可分为两类
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tBuqm5Hv-1678672774052)(C:\Users\ADMIN\AppData\Roaming\Typora\typora-user-images\image-20220829175425328.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VtO1K2ie-1678672774053)(C:\Users\ADMIN\AppData\Roaming\Typora\typora-user-images\image-20220829180346949.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-azTRqOb5-1678672774053)(C:\Users\ADMIN\AppData\Roaming\Typora\typora-user-images\image-20220829180637652.png)]
NullPhinterException空指针异常
当应用程序试图在需要对象的地方使用null时,抛出该异常,看案例演示。
public class NullPointerException {
public static void main(String[] args) {
String name = null;
System.out.println(name.length());
}
}
Exception in thread "main" java.lang.NullPointerException
at exception_.NullPointerException.main(NullPointerException.java:10)
ArithmeticException数字运算异常
当出现异常的运算条件时,抛出此异常。例如,一个整数“除以零”时,抛出此类的一个实例,案例演示:
public class ArithmeticException {
public static void main(String[] args) {
int num = 1/0;
System.out.println(num);
}
}
Exception in thread "main" java.lang.ArithmeticException: / by zero
at exception_.ArithmeticException.main(ArithmeticException.java:9)
ArrayIndexOutOfBoundsException数组下标越界异常
用非法索引访问数组时抛出的异常。如果索引为负或大于等于数组大小,则该索引为非法索引
public class ArrayIndexOutOfBoundsException {
public static void main(String[] args) {
int[] arr = {1,2,4};
for (int i = 0; i < arr.length + 1; i++) {
System.out.println(arr[i]);
}
}
}
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 3
at exception_.ArrayIndexOutOfBoundsException.main(ArrayIndexOutOfBoundsException.java:11)
ClassCastException类型转换异常
当试图将对象强制转换为不是实例的子类时,抛出该异常。案例演示:
public class ClassCastExption {
public static void main(String[] args) {
A a = new B();
B b = (B)a;
C c = (C)a;
}
}
class A{ }
class B extends A{ }
class C extends A{ }
Exception in thread "main" java.lang.ClassCastException: exception_.B cannot be cast to exception_.C
at exception_.ClassCastExption.main(ClassCastExption.java:11)
NumberFormatException数字格式不正确异常[]
当应用程序试图将字符串转换成一种数值类型,但该字符串不能转换为适当格式时,抛出该异常=>使用异常我们可以确保输入是数字的条件
public class NumberFormatException {
public static void main(String[] args) {
String name = "dsj";
System.out.println(Integer.parseInt(name));
}
}
Exception in thread "main" java.lang.NumberFormatException: For input string: "dsj"
at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
at java.lang.Integer.parseInt(Integer.java:580)
at java.lang.Integer.parseInt(Integer.java:615)
at exception_.NumberFormatException.main(NumberFormatException.java:10)
介绍
编译异常是指在编译期间,就必须处理的异常,否则代码不能通过编译。
常见的编译异常
SQLException
操作数据库时,查询表可能发生异常
IOException
操作文件时,发生的异常
FileNotFoundException
当操作一个不存在的文件时,发生异常
ClassNotFoundException
加载类,而该类不存在时,发生异常
illegalArguementException
参数异常
基本介绍
异常处理就是当异常发生时,对异常处理的方式
异常处理
try-catch-finally
程序员在代码中捕获发生的异常,自行处理
throws
将发生的异常抛出,交给调用者(方法)来进行处理,最顶级处理者就是JVM
如果程序员没有进行异常处理,程序默认使用throws
Java提供try和catch块来处理异常。try块用于包含可能出错的代码。catch用于处理try块中的异常。可根据需要嵌入多个try-catch块
如果异常发生了,则异常后面的代码不会执行,直接进入到catch块。
如果异常没用发生,则顺序执行try的代码块,不会进入到catch。
如果希望不管是否发生异常,都执行某段代码(比如关闭连接,释放资源等)则使用finally{ }
可以有多个catch语句,捕获不同的异常(进行不同的业务处理),要求父类异常在后,子类异常在前,比如(Exception在后,NullPointerException在前),如果发生异常,只会匹配一个catch,案例演示:
try{
}catch(NullPointerException e){
}catch(Exception e){
}finally{
}
可以进行try-finally配合使用,这种用法相当于没用捕获异常,因此程序会直接崩掉。
应用场景:执行一段代码,不管是否发生异常,都必须执行某个业务逻辑。
若存在finally,即使前面的代码已经return,一定会执行finally中的代码,其过程相当于递归调用
基本介绍
如果一个方法中的语句执行时,可能生成某种异常,但是并不能确定如何处理这种异常,则此方法应显示地声明抛出异常,表明该方法将不对这些异常进行处理,而由该方法的调用者负责处理。
在方法声明中用throws语句可以声明抛出异常的列表,throws后面的异常类型可以是方法中产生的异常类型,也可以是它的父类。
public class Throws01 {
public static void main(String[] args) {
}
public void f1() throws FileNotFoundException, java.lang.NullPointerException, java.lang.ArithmeticException {
//创建了一个文件流对象
//老韩解读:
//1.这里的异常是一个FileNotFoundException编译异常
//2.使用前面讲过的try-catch-finally
//3.使用throws,抛出异常,让调用f1方法的调用者(方法)处理
//4.throws后面的异常类型可以是方法中产生的异常类型,也可以是它的父亲
//5.throws关键字后也可以是异常列表,即可以抛出多个异常
FileInputStream fis = new FileInputStream(("d://aa.txt"));
}
}
注意事项
基本概念
当程序种出现了某些“错误”,但该错误信息并没有再Throwable子类种描述处理,这个时候可以自己设计异常类,用于描述该错误信息。
自定义异常的步骤
throws:
public void f1() throws Exception{
...
}
throw:
if(age>=200){
throw new AgeException("年龄需要小于200");
}
先使用传统的方法来解决 -> 引出泛型
package com.dsj.generic_;
import java.util.ArrayList;
/*
* @author 远空_
* @version 1.0
* */
public class Generic01 {
public static void main(String[] args) {
ArrayList list = new ArrayList();
list.add(new Dog("旺财",10));
list.add(new Dog("发财",1));
list.add(new Dog("小白",20));
//加入我们程序员不小心添加了一只猫
list.add(new Cat("招财",1));
//遍历
for (Object o : list) {
Dog dog = (Dog) o;
System.out.println(dog.getName() + "-" + dog.getAge());
}//ClassCastException
}
}
class Dog{
public String name;
public int age;
public Dog(String name,int age){
this.name=name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Dog{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
class Cat{
public String name;
public int age;
public Cat(String name,int age){
this.name=name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Cat{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
package com.dsj.generic_.imporve;
import java.util.ArrayList;
/*
* @author 远空_
* @version 1.0
* */
public class Generic02 {
public static void main(String[] args) {
//1.当我们ArrayList存放到ArrayList集合中的元素是Dog类型
//2.如果编译器发现添加的类型不满足要求,就会报错
//3.在遍历的时候,可以直接取Dog类型而不是Object
ArrayList<Dog> list = new ArrayList<Dog>();
list.add(new Dog("旺财",10));
list.add(new Dog("发财",1));
list.add(new Dog("小白",20));
//加入我们程序员不小心添加了一只猫
// list.add(new Cat("招财",1));
//遍历
for (Dog dog : list) {
System.out.println(dog.getName() + "-" + dog.getAge());
}//ClassCastException
}
}
class Dog{
public String name;
public int age;
public Dog(String name,int age){
this.name=name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Dog{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
class Cat{
public String name;
public int age;
public Cat(String name,int age){
this.name=name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Dog{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
优势
不使用泛型
Dog -> Object ->Dog//放入到ArrayList会先转成Objcet,在取出时,还需要转换成Dog
使用泛型
Dog->Dog->Dog//放入时,和取出时,不需要类型转换提高效率
3.不再提示编译警告
如同变量:int a = 10;
泛型=>Integer,Sting,Dog
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mSFg0BYj-1678672774054)(C:\Users\ADMIN\AppData\Roaming\Typora\typora-user-images\image-20221012104838139.png)]
泛型又称参数化类型,是JDK5出现的新特性,解决数据类型的安全问题
在类声明或实例化时只需要制定好需要的类型即可
JAVA泛型可以保证如果程序在编译时没有法储警告,运行时就不会产生ClassCastException异常。同时,代码更简介、健壮
泛型的作用是:可以在类声明时通过一个表示表示类中某个属性的类型,或者是某个方法的返回值类型,或者是参数类型。
package com.dsj.generic_;
/*
* @author 远空_
* @version 1.0
* */
public class Generic03 {
public static void main(String[] args) {
//特别强调:E具体的数据类型在定义Person对象的时候指定,在编译期间,就确定E是什么类型
Person<String> dsj = new Person<>("dsj");
Person<Integer> integerPerson = new Person<Integer>(100);
}
}
class Person<E>{
E s;//E表示s的数据类型,该数据类型在定义Person对象的时候指定,即在编译期间,就确定E是什么
public Person(E s) {//E也可以是参数类型
this.s = s;
}
public E f(){//返回类使用E
return s;
}
}
泛型的声明
interface 接口<T>和class 类<K,V>{}
//比如:List,ArrayList
//说明:1)其中,T,K,V不代表正常值,而是表示类型
// 2)任意字母都可以。常用T表示,是Type的缩写
泛型的实例化
注意事项
interface List{},public class HashSet{}…等等
说明:T,E只能是引用类型,不能是基本数据类型
在指定泛型具体类型后,可以传入该类型或者其子类型类型
泛型简写使用形式(推荐第二种)
List list1 = new ArrayList();
List list2 = new ArrayList<>();
若默认不写,则E默认传入Object
基本语法
class 类名<T,R,...){
成员
}
注意细节
普通成员可以使用泛型(属性、方法)
使用泛型的数组,不能初始化
T[] array = new T[8]//false,因为数组在new的时候不能确定T的类型,无法确定空间大小,无法进行空间开辟
静态方法不能使用类的泛型(泛型是在new对象的时候定义的)
public static void m1(T t){}//false,因为静态是和类相关的,在类加载时,对象还没有创建,所以,如果静态方法和静态属性使用了泛型,JVM就无法完成初始化
泛型类的类型,是在创建对象时确定的(因为创建对象时,需要指定类型)
如果在创建类对象时,没有指定类型,默认为Object
基本语法
interface 接口名<T,R...>{
}
注意细节
基本语义
修饰符 返回类型 方法名(参数列表){
}
注意细节
泛型方法,可以定义在普通类中,也可以定义在泛型类中
class Car{
public void run(){
}
//说明
//1.就是泛型
//2.提供给fly使用的
public <T,R> void fly(T t,R r){
}
}
class Fish<T,R>{//泛型类
public void run(){
}
public <U,M> void eat(U u,M m){
}
}
当泛型方法被调用时,类型会确定。
public void eat(E e){},修饰符后没有
泛型方法,可以使用类声明的泛型,也可以使用自己声明的泛型
class Fish<T,R>{//泛型类
public void run(){
}
public <U,M> void eat(U u,M m){
}
//1.下面的hi()并不是泛型方法
//2.是hi()使用了类声明的泛型变量
public void hi(T t){
}
//3.泛型方法,可以使用类声明的泛型,也可以使用自己声明的泛型
public <K> void hello(R r,K k){
}
}
泛型不具备继承性
List <Object> list = new ArrayList<String> ();//false
public static void printList(List<?> list) {
for (Object o : list) {
System.out.println(o);
}
}
public static void main(String[] args) {
List<String> l1 = new ArrayList<>();
l1.add("aa");
l1.add("bb");
l1.add("cc");
printList(l1);
List<Integer> l2 = new ArrayList<>();
l2.add(11);
l2.add(22);
l2.add(33);
printList(l2);
}
(32条消息) 泛型(T)、通配符(?)理解和区别_华海的bk的博客-CSDN博客_泛型通配符?和泛型t区别
说明:
通配符其实是用于传入实参的时候,对实参类型的限制。
为什么需要Junit
基本介绍
除了前两个,后面的都会继承Number类【可通过结构图进行查看】
//手动装箱 int -> Integer
int n1 = 100;
Integer integer = new Integer(n1);
System.out.println(integer);//100
Integer integer1 = Integer.valueOf(n1);
System.out.println(integer1);//100
System.out.println(integer==integer1);//false,不是同一个对象
//手动拆箱 Integer -> int
Integer integer2 = new Integer(200);
int i = integer2.intValue();
System.out.println(i);//200
public static void main(String[] args) {
Integer i = new Integer(1);
Integer j = new Integer(1);
System.out.println(i == j);
Integer m = Integer.valueOf(1);
Integer n = 1;
System.out.println(m == n);
Integer x = 128;
Integer y = 128;
System.out.println(x == y);
}
String对象用于保存字符串,也就是一组字符串序列
字符串常量对象是用双引号阔气的字符序列。例如:“你好”、“12.97”、“boy”等
字符串的字符使用Unicode字符编码,一个字符(不区分字母还是汉字)占两个字节
String类较常用构造方法(其它看手册):
String s1 = new String();
String s2 = new String(String original);
String s3 = new String(char[] a);
String s4 = new String(char[] a,int startIndex,int count);
String类实现了接口Serializable[String可以串行化:可以在网络传输]
String是final类,不能被其它的类继承
String 有属性private final char value[];用于存放字符串的内容【⭐一定要注意:value是一个final类型,不可以修改!即value不能指向新的地址,但是单个字符内容是可以变化的!】
方式1:直接赋值String s = “dsj”;
方式2:调用构造器 String s = new String(“dsj”);
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qekSw2pf-1678672774056)(C:\Users\ADMIN\AppData\Roaming\Typora\typora-user-images\image-20220903195221184.png)]
//以下语句创建了几个对象?画出内存布局图
String s1 = "hello";
s1 = "haha";
//创建了两个对象
//以下语句创建了几个对象?
String a = "hello" + "abc";
//一个对象
//解读:编译器会进行优化,判断创建的常量池对象,是否有引用指向
//等价于 String a = "helloabc";
//创建了几个对象?画出内存图。
String a = "hello";
String b = "abc";
String c = a + b;
//解读
//1.先创建一个StringBuilder sb = new 先创建一个StringBuilder()
//2.执行sb.append("hello");
//3.sb.append("abc");
//4.String c = sb.toString();
// public String toString() {
// // Create a copy, don't share the array
// return new String(value, 0, count);
// }
//最后其实是c指向堆种的对象(String) value[] 再指向 池中 "helloabc"
//一共创建3个对象,如下图
//小结:
//底层是StringBuilder sb = new StringBuilder();sb.append(a);sb.append(b);sb是在堆种,并且append是在原来字符串的基 础上追加的。
//重要规则:String c1 ="ab" + "cd";常量相加,看的是池。String c1 = a + b;变量相加,是在堆中
//下面代码输出说明,并说明原因
public class StringExercise09 {
public static void main(String[] args) {
Test1 ex = new Test1();
ex.change(ex.str,ex.ch);
System.out.print(ex.str+" and ");
System.out.println(ex.ch);
}
}
class Test1{
String str = new String("dsj");
final char ch[] = {'j','a','v','a'};
public void change(String str,char ch[]){
str = "java";
ch[0] = 'h';
}
}
//输出 dsj and hava
说明
String类是保存字符串变量的。每次更新都需要重新开辟空间,效率较低,因此java设计者还提供了StringBuilder和StringBuff来增强String功能,并提高效率。【后面外面还会详细介绍StringBuilder和StringBuffer】
String s = new String("");
for(int i = 0; i < 80000; i++){
s += "hello";
}
equals // 区分大小写,判断内容是否相等
equalsIgnoreCase //忽略大小写的判断内容是否相等
length //获取字符的个数,字符串的长度
indexOf //获取字符在字符串中第一次出现的索引,索引从0开始,如果找不到,返回-1
lastIndexOf //获取字符在字符串中最后一次出现的索引,索引从0开始,如果找不到,返回-1
substring //截取指定范围的子串
trim //去前后空格
charAt //获取索引某处的字符,注意不能使用Str[index]这种方式
toUpperCase //转换成大写
toLowerCase //转换成小写
concat //拼接,将字符串拼接起来
s.concat("林黛玉").concat("薛宝钗").concat("together");
replace //替换,替换字符串中的字符,并返回一个新的字符串对象的引用
s = "宝玉 and 林黛玉 薛宝钗 薛宝钗"
s1 = s.replace("林黛玉","薛宝钗");
//将s中所有“林黛玉”替换成“薛宝钗”,赋值给s1
split 分割字符串,对于某些分割字符,外面需要转义 比如 | 和\ \等,并返回一个数组对象的引用【在对字符串进行分割时,如果有特殊字符,需要加入转义符\】
String poem = "锄禾日当午,汗滴禾下土,谁知盘中餐,粒粒皆辛苦";
String split[] = poem.split(",");
//以,为标准,将字符串进行分割,返回一个数组
String poem = "E:\\aaa\\bbb";
String split[] = poem.split("\\\\");
toCharArray 转换成字符数组
String s = "happy";
char chs[] = s.toCharArray();
compareTo比较两个字符串的大小,如果前者大,则返回整数,后者大,则返回负数,如果相等,则返回0[建议看源码]
String a = "john";
String b = "jack";
format 格式化字符串
占位符有:
%s 字符串 %c 字符 %d 整形 %.2f 浮点型
String name = "john";
int age = 10;
double score = 98.3 / 3;
char gender = '男';
//传统写法
String info = "我的名字是" + name + ",年龄是" + age + ",成绩是" + score + ",性别是" + gender +"。希望等级喜欢我!";
//占位符写法
String info = String.format("我的名字是%s,年龄是%d,成绩是%.2f,性别是%c。希望大家喜欢我",name,age,score,gender);
基本介绍
java.lang.StringBuffer代表可变的字符序列,可以堆字符串内容进行增删。很多方法与String相同,但StringBuffer是可变长度的。StringBuffer是一个容器。
StringBuffer VS String
构造器详解
StringBuffer()
//构造一个其中不带字符的字符串缓冲区,其初始容量为16个字符。
StringBuffer(CharSequence seq)
//构造一个字符串缓冲区,它包含与指定的CharSequence相同的字符。
StringBuffer(int capacity)
//构造一个不带字符,但具有指定初始容量的字符串缓冲区。即对char[]带下进行指定
StringBuffer(String str)
//构造一个字符串缓冲区,并将其内容初始化为指定的字符串内容。
String和StringBuffer互相转换
在开发中,我们经常需要将String和StringBuffer进行转换
//String->StringBuffer
String s = "hello";
//方式1
StringBuffer b1 = new StringBuffer(s);
//方式2
StringBuffer b2 = new StringBuffer();
b2.append(s);
//StringBuffer -> String
//方式1
String s2 = b1.toString();//指向常量池
//方式2
String s3 = new String(b1);//指向value[]
StringBuffer类方法
StringBuffer s = new StringBuffer("hello");
//增
s.append(',');
s.append("张三丰");
s.append("赵敏").append(100).append(true).append(10.5);
System.out.println(s);//hello,张三丰赵敏100true10.5
//删
//解读:删除[11,14)范围的字符
s.delete(11,14);
System.out.println(s);//hello,张三丰赵敏true10.5
//改
//范围[9,11)
s.replace(9,11,"周芷若");
System.out.println(s);//hello,张三丰周芷若true10.5
//查
int indexOf = s.indexOf("张三丰");
System.out.println(indexOf);//6
//插入
//索引为9的位置插入"赵敏",原先索引为9的内容自动后移
s.insert(9,"赵敏");
System.out.println(s);//hello,张三丰赵敏周芷若true10.5
//长度
System.out.println(s.length());//22
比较
StringBuilder和StringBuffer非常类似,均代表可变的字符序列,而且方法也一样
String:不可变字符序列,效率很低,但是复用率高
StringBuffer:可变字符序列、效率较高(增删)、线程安全
StringBuilder:可变字符序列、效率最高、线程不安全
String使用注意说明:
String s = "a";//创建了一个字符串
s += "b";//实际上原来的"a"字符串对象已经丢弃了,现在又产生了一个字符串s+"b"(也就是"ab")如果多次执行这些改变串内容的操作,会导致大量副本字符串留在内存中,降低效率。如果这样的操作放到循环中会极大地影响程序的性能=>结论:如果我们堆String做大量修改,不要使用String
选择
StringBuilder的方法使用和StringBuffer一样,不再说。
Math类包含用于执行基本数学运算的方法,如初等值数、对数、平方根和三角函数。
方法一览
abs绝对值
int abs = Math.abs(-9);
pow求幂
double pow = Math.pow(2,4);//2的4次方
ceil取整,向上取整,返回>=该参数的最小整数
double ceil = Math.ceil(-3.0001);//-3
floor 向下取整,返回<=该参数的最大整数
double floor = Math.floor(-4.999);//-5
round 四舍五入,相当于给原数+0.5
long round = Math.round(-5.001);//-4
sqrt求开方
double sqrt = Math.sqrt(-9);//不存在返回null
double sqrt = Math.sqrt(9);//3
random求随机数
//random 返回的是0 <= x <1 之间的一个随机小数
//思考,如何返回一个a-b之间的一个随机整数,a,b俊伟整数,比如a = 2;b = 7;
//(int)(a) <= x <= (int)(a + Math.random()*(b-a+1)),加1是因为向下取整
max、min返回最大最小值
int min = Math.min(1,9);//1
int max = Math.max(45,90);//45
Arrays里面包含了一系列静态方法,用于管理或操作数组(比如排序和搜索)
常用方法
toString返回数组的字符串形式
Arrays.toString(arr);
sort排序(自然排序和定制排序)
Integer arr[] = {1,-1,7,0,89};
Arrays.sort(arr);
//因为数组是引用类型,所以通过sort排序后,会直接影响到实参arr
//sort有重载的方法,也可以通过传入 一个接口Comparator实现 定制排序
Arrays.sort(arr,new Comparator(){
@Override
public int compare(Object o1,Object o2){
Integer i1 = (Integer)o1;
Integer i2 = (Integer)o2;
return i2 - i1;//这里体现接口编程,看看源码,就明白
}
});
//调用 定制排序 时,传入两个参数(1)排序的数组arr(2)实现了Comparator接口的匿名内部类
binarySearch 通过二分查找法进行查找,要求必须排好序
//1.使用binarySearch 二叉查找
//2.要求该数组是有序的。如果该数组是无需的,不能使用binarySearch
//3.如果数组中不存在该元素,就返回-1
int index = Arrays.binarySearch(arr,3);
copyOf数组元素的复制
//1.从arr数组中,拷贝arr.length个元素到newArr数组中
//2.如果拷贝的长度>arr.length就在新数组后面增加null值
//3.如果拷贝长度<0就抛出异常NegativeArraySizeException
Integer newArr[] = Arrays.copyOf(arr,arr.length);
fill数组元素的填充
Integer num[] = new Integer[]{9,3,2};
//1.使用99去填充num数组,可以理解成是替换原来的元素
Arrays.fill(num,99);
equals比较两个数组元素内容是否完全一致
Integer arr[] = {1,-1,7,0,89};
Integer arr2[] = {1,2,90,123,567};
//1.如果arr和arr2数组的元素一样,则返回true
boolean equals = Arrays.equals(arr,arr2);
asList 将一组值,转换成list
//1.asList方法,会将(2,3,4,5,6,1)数据转成一个List集合
//2.返回的 asList 编译类型 List(接口)
//3.asList 运行类型:class java.util.Arrays#ArrayList,是Array的一个静态内部类
List asList = Array.asList(2,3,4,5,6,1);
sout("asList="+asList);//asList=[2,3,4,5,6,1]
exit退出当前程序
sout("ok1");
//1.exit(0)表示程序退出
//2.0表示一个状态,正常的状态
System.exit(0);
sout("ok2");
arraycopy:复制数组元素,比较适合底层调用,一般使用Arrays.copyOf完成复制数组。
int src[] = {1,2,3};
int dest[] = new int[4];
//1.主要是搞清楚5个参数的含义
//* @param src the source array.源数组
//* @param srcPos starting position in the source array.从源数组的哪个索引开始拷贝
//* @param dest the destination array.目标数组
//* @param destPos starting position in the destination data.把源数组的数据拷贝到目标数组的哪个开始索引
//* @param length the number of array elements to be copied.被拷贝的个数
System.arraycopy(src,0,dest,1,3);
System.out.println("dest="+ Arrays.toString(dest));//dest=[0,1,2,3]
currentTimeMillens:返回当前事件距离1970-1-1的毫秒数
System.out.println(System.currentTimeMillis());
gc:运行垃圾回收机制System.gc()
不作演示
public static void main(String[] args) {
long l = 23788888899l;
System.out.println("l="+l);
BigInteger bigInteger = new BigInteger("23333333333333333333333333333333333333333333333");
BigInteger bigInteger1 = new BigInteger("100");
System.out.println(bigInteger);//23333333333333333333333333333333333333333333333
//1.在对BigInteger进行加减乘除的时候,需要使用对应的方法,不能直接进行加减乘除
//2.可以创建一个要操作的BigInteger然后进行相应操作
BigInteger add= bigInteger.add(bigInteger1);
System.out.println(add);//23333333333333333333333333333333333333333333433
BigInteger subtract = bigInteger.subtract(bigInteger1);//减
System.out.println(subtract);//23333333333333333333333333333333333333333333233
BigInteger multiply = bigInteger.multiply(bigInteger1);//乘
System.out.println(multiply);//2333333333333333333333333333333333333333333333300
BigInteger divide = bigInteger.divide(bigInteger1);//除
System.out.println(divide);//233333333333333333333333333333333333333333333
}
Date类:精确到毫秒,代表特定的瞬间
SimpleDateFormat:格式和解析日期的具体类。它允许进行格式化(日期 -> 文本)、解析(文本 -> 日期)和规范化。
应用实例
public static void main(String[] args) throws ParseException {
//1.获取当前系统事件
//2.这里的Date类是在java.util包
//3.默认输出的日期格式是国外的方式,因此通常需要对格式进行转换
Date d1 = new Date();//获取当前系统事件
System.out.println("当前日期="+d1);
Date d2 = new Date(99999);//通过指定毫秒数得到时间
System.out.println(d1.getTime());//获取某个时间对应的毫秒数
//1.创建 SimpleDateFormat对象,可以指定相应的格式
//2.这里的格式使用的字母是规定好的
SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 hh:mm:ss E");
String format = sdf.format(d1);//format():将日期转换成指定格式的字符串
//1.可以把一个格式化的String转换成对应的Date
//2.得到Date仍然在输出时,还是按照国外的形式,如果希望指定格式输出,需要转换
//3.在把String -> Date,使用的sdf格式需要和你给的String的格式一样,否则会抛出转换异常
String s = "1996年01月01日 10:20:30 星期一";
Date parse = sdf.parse(s);
System.out.println(parse);
System.out.println(sdf.format(parse));
}
第二代日历类,主要就是Calendar类(日历)
public abstract class Calendar extends Object implments Serializable,Cloneable,Comparable<Calendar>
Calendar类是一个抽象类,它为特定瞬间与一组注入YEAR、MONTH、DAY_OFMONTH、HOUR等日历字段之间的转换提供了一些方法,并为操作日历字段(例如获得下星期的日期)提供了一些方法。
实例演示
public static void main(String[] args) {
//1.Calendar是一个抽象类,并且构造器是private,所以new不出来,通过静态属性和方法来使用
//2.可以通过getInstance()来获取实例
//3.提供大量的方法和字段提供给程序员
//4.Calendar没有提供对应的格式化的累,因此需要程序员自己组合来输出
//5.⭐如果我们需要按照24小时进制来获取时间,Calendar.Hour ==>Calendar.HOUR_OF_DAY
Calendar c = Calendar.getInstance();//创建日历类对象,比较自由简单
System.out.println(c);
//获取日历对象的某个日历字段
System.out.println("年:"+c.get(Calendar.YEAR));
System.out.println("月:"+c.get(Calendar.MONTH)+1);
System.out.println("日:"+c.get(Calendar.DAY_OF_MONTH));
System.out.println("小时:"+c.get(Calendar.HOUR));
System.out.println("分钟:"+c.get(Calendar.MINUTE));
System.out.println("秒:"+c.get(Calendar.SECOND));
//Calendar没有专门的格式化方法,所以需要程序员自己来组合显示
System.out.println(c.get(Calendar.YEAR)+"年"+(c.get(Calendar.MONTH)+1)+"月"+c.get(Calendar.DAY_OF_MONTH)+"日");
}
前面两代日期类的不足分析
JDK1.0中包含了一个java.util.Date类,但是它的大多数方法已经在JDK1.1引入Calendar类之后被弃用了。而Calendar也存在的问题是:
第三代日期类
案例演示
public static void main(String[] args) {
//1.使用now()返回表示当前日期时间的对象
LocalDateTime ldt = LocalDateTime.now();
System.out.println(ldt);
System.out.println("年="+ldt.getYear());
System.out.println("月(英文)="+ldt.getMonth());
System.out.println("月(数字)="+ldt.getMonthValue());
System.out.println("日="+ldt.getDayOfMonth());
System.out.println("时="+ldt.getHour());
System.out.println("分="+ldt.getMinute());
System.out.println("分="+ldt.getSecond());
//只关心年月日
LocalDate now = LocalDate.now();
System.out.println(now);
}
DateTimeFormatter格式日期类
类似于SimpleDateFormat
DateTimeFormat dtf = DateTimeFormatter.ofPattern(格式);
String str = dtf.format(日期对象);
案例演示:
public static void main(String[] args) {
//1.使用now()返回表示当前日期的对象
LocalDateTime ldt = LocalDateTime.now();
System.out.println(ldt);
//2.使用DateTimeFormatter对象来进行格式化
//创建 DateTimeFormatter对象
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH小时mm分钟ss秒 E");
String format = dtf.format(ldt);
System.out.println(format);
}
类似于Date提供了一系列和Date类转换的方式
Instant–>Date:
Date date = Date.from(instant);
Date–>Instant:
Instant instant = date.toInstant();
案例演示
public static void main(String[] args) {
//1.通过 静态方法 Instant.now() 返回Instant时间戳对象
Instant now = Instant.now();
System.out.println(now);
//2.通过Date.from()可以把Instant转成Date对象
Date date = Date.from(now);
//3.通过Instant.toInstant()可以把Date转成Instant对象
Instant instant = date.toInstant();
}
数组
集合
⭐集合的框架体系图
Collection接口实现类的特点
public interface Collection<E> extends Iterable<E>
常用方法,以实现子类ArrayList来演示
add:添加单个元素(返回布尔值)
list.add("jack");
list.add(10);//list.add(new Inerger(10))
list.add(true);//会自动装包
System.out.println("list = "+list);//list = [jack, 10, true]
remove:删除指定元素(返回布尔值)
list.remove(1);//删除第二个元素
System.out.println("list = "+list);//list = [jack, true]
list.remove("jack");//删除指定某个元素
System.out.println("list = "+list);//list = [true]
contains:查找元素是否存在(返回布尔值)
System.out.println(list.contains("jack"));//false
System.out.println(list.contains(true));//true
size:获取元素个数(返回int)
System.out.println(list.size());//返回1
isEmpty:判断是否为空(返回布尔值)
System.out.println(list.isEmpty());//false
clear:清空(void)
list.clear();
System.out.println("list = "+list);//list = []
addAll:添加多个元素(boolean)
List list2 = new ArrayList();
list2.add("红楼梦");
list2.add("三国演义");
list.addAll(list2);//传入实现Collection的集合
System.out.println("list = "+list);//list = [true, 红楼梦, 三国演义]
containsAll:查找多个元素是否都存在(boolean)
System.out.println(list.containsAll(list2));//true
removeAll:删除多个元素(boolean)
list.add("聊斋");
list.removeAll(list2);
System.out.println("list = "+list);//list = [true, 聊斋]
基本介绍
Iterator对象称为迭代器,主要用于遍历Collection集合中的元素
所有实现了Collection接口的集合类都有一个iterator()方法,用以返回一个实现了Iterator接口的对象,即可以返回一个迭代器
Iterator的结构:
Iterator仅用于遍历集合,Iterator本身并不存放对象
基本介绍
增强for循环,可以代替iterator迭代器,特点:增强for就是简化版的iterator,本质一样。只能用于遍历集合或数组。
基本语法
for(元素类型 元素名 : 鸡和马或数组名){
访问元素
}
因为Set接口是无序的,不提供get方法通过索引获取,因此无法使用普通for进行遍历
for(int i = 0; i< list.size();i++){
Object object = list.get(i);
System.out.println(object);
}
List接口基本介绍
List接口是Collection接口的子接口
List集合类中元素有序(即添加顺序和去除顺序一致)、且可重复
List list = new ArrayList();
list.add("tom");
list.add("jack");
list.add("mary");
list.add("mary");
list.add("smith");
System.out.println(list);//[tom, jack, mary, mary, smith]顺序一致,可重复
List集合中的每个元素都有其对应的顺序索引,即支持索引
System.out.println(list.get(3));//索引从0开始,返回mary
List容器中的元素都对应一个整数型的序号记载其在容器中的位置,可以根据序号存取容器中的元素
JDK API中List接口的实现类有:
List接口的常用方法
List集合里添加了一些根据索引来操作集合元素的方法
void add(int index,Object ele):在index位置插入ele元素
List list = new ArrayList();
list.add("张三丰");
list.add("贾宝玉");
list.add(1,"邓圣君");
boolean addAll(int index,Collection eles):从index位置开始将eles中的所有元素添加进来
List list2 = new ArrayList();
list2.add("jack");
list2.add("tom");
list.addAll(1,list2);//在dsj后面加入list2的元素
System.out.println(list);//[张三丰, jack, tom, 邓圣君, 贾宝玉]
Object get(int index):获取指定index位置的元素
不解释
int indexOf(Object obj):返回obj在集合中首次出现的位置
System.out.println(list.indexOf("tom"));//2
int lastIndexOf(Object obj):返回obj在集合中末次出现的位置
list.add("邓圣君");
System.out.println(list.lastIndexOf("邓圣君"));//5
Object remove(int index):移除指定index位置的元素,并返回此元素
System.out.println(list.remove(0));//张三丰
System.out.println(list);//[jack, tom, 邓圣君, 贾宝玉, 邓圣君]
Object set(int index,Object ele):设定指定index位置的元素为ele,相当于是替换
list.set(1,"玛丽");//索引必须存在,因为是“替换”
System.out.println(list);//[jack, 玛丽, 邓圣君, 贾宝玉, 邓圣君]
List subList(int fromIndex,int toIndex):返回从fromIndex到toIndex位置的子集合
List res = list.subList(0,2);//注意,返回的子集合是[0,2)并非闭区间
System.out.println(list);
System.out.println(res);
ArrayList中维护了一个Object类型的数组elementDate
transient Object[] elementDate;
当创建对象时,如果使用的是无参构造器,则初始elementDate容量为0(jdk7是10)
当添加元素时:先判断是否需要扩容,如果需要扩容,则调用grow()方法,否则直接添加元素到合适位置
如果使用的是无参构造器,如果第一次添加,需要扩容的话,则扩容elementData为10,如果需要再次扩容的话,则扩容elementData为1.5倍
如果使用的是指定容量capacity的构造器,则初始elementData容量为capacity
如果使用的是指定容量capacity的构造器,如果需要扩容,则直接扩容为elementData为其1.5倍
无参构造器过程
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Zox0EZDr-1678672774057)(C:\Users\ADMIN\AppData\Roaming\Typora\typora-user-images\image-20220916132015869.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ez6EWUDK-1678672774058)(C:\Users\ADMIN\AppData\Roaming\Typora\typora-user-images\image-20220916132031919.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jdfszUVs-1678672774059)(C:\Users\ADMIN\AppData\Roaming\Typora\typora-user-images\image-20220916132042455.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-X5F50ece-1678672774060)(C:\Users\ADMIN\AppData\Roaming\Typora\typora-user-images\image-20220916132109475.png)]
有参构造器过程
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-M1jtnx1H-1678672774061)(C:\Users\ADMIN\AppData\Roaming\Typora\typora-user-images\image-20220916184146802.png)]
Vector类的定义说明
Vector底层也是一个对象数组,protected Object[] elementData;
Vector是线程同步的,即线程安全,Vector类的操作方法有synchronized
public synchronized E get(int index){
if(index >= elementCount)
throw new ArrayIndexOutOfBoundsExecption(index);
return elementData(index);
}
在开发中,需要线程同步安全时,考虑使用Vector
自己看jdk
LinkedList实现了双向链表和双端队列特点
图1-双向链表
可以添加任意元素(元素可以重复),包括null
线程不安全,没有实现同步
public class LinkList01 {
public static void main(String[] args) {
Node jack = new Node("jack");
Node tom = new Node("tom");
Node dsj = new Node("dsj");
// 连接3个节点,形成双向链表
//jack -> tom -> dsj
jack.next = tom;
tom.next = dsj;
//dsj -> tom ->jack
dsj.prev = tom;
tom.prev = jack;
Node first = jack;//让first引用指向jack,就是双向链表的头节点
Node last = dsj;//让last引用指向dsj,就是双向链表的尾节点
//演示,从头到尾遍历
while (first != null){
System.out.println(first);
first = first.next;
}
//演示,从尾到头遍历
while (last != null){
System.out.println(last);
last = last.prev;
}
//链表插入数据
first = jack;
first = Node.insert(2,new Node("sora"),first);//插入"sora"到第二个节点
while (first != null){
System.out.println(first);
first = first.next;
}
}
}
//定义一个Node类,Node对象表示双向链表的一个节点
class Node{
public Object item;//存放数据
public Node next;//指向下一个节点
public Node prev;//指向上一个节点
public Node(Object data){
this.item = data;
}
@Override
public String toString() {
return "Node{" +
"item=" + item +
'}';
}
public static Node insert(int index,Node newNode,Node first){
Node flag = first;
for (int i = 0; i < index; i++) {
if(first == null){
throw new RuntimeException("first 节点不能为 NULL");
}
first = first.next;
}
if(first.prev != null){
newNode.prev = first.prev;
first.prev.next = newNode;
}
newNode.next = first;
first.prev = newNode;
if(index == 0){
flag = flag.prev;
}
return flag;
}
}
自己看jdk
无序(添加和取出的顺序不一致),没有索引,虽然取出的顺序不是添加的顺序,但是取出的顺序是固定的
不允许重复元素,所以最多包含一个null(不会覆盖原来的元素,只会返回布尔值)
set.add("jack");//return true;
set.add("jack");//return false;
JDK API中Set接口的实现类有:
HashSet实现了Set接口
HashSet实际上是HashMap,看下源码。
可以存放null值,但是只能有一个null,不能重复
HashSet不保证元素是有序的,取决于hash后,再确定索引结果
不能有重复元素/对象。在前面Set接口使用已经讲过。
仅存放Key值,因为底层使用HashMap,Value值是一个常量,仅Key值是有用的
HashSet底层是HashMap,HashMap底层是(数组+链表+红黑树)
public class HashSetStructure {
public static void main(String[] args) {
//模拟一个HashSet的底层
//1.创建一个数组,数组的类型是Node[]
//2.有些人,直接把 Node[] 数组称为 表table
Node table[] = new Node[16];
System.out.println("table = "+table);
//3.创建节点
Node john = new Node("john", null);
table[2] = john;
System.out.println("table = "+table);
Node jack = new Node("jack", null);
john.next = jack;//将jack节点挂载到john
System.out.println("table = "+table);
}
}
class Node{
Object item;//存放数据
Node next;//指向下一个节点
public Node(Object item, Node next) {
this.item = item;
this.next = next;
}
@Override
public String toString() {
return "Node{" +
"item=" + item +
", next=" + next +
'}';
}
}
public class HashSet_ {
public static void main(String[] args) {
HashSet hashSet = new HashSet();
/*
* final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
Node[] tab; Node p; int n, i;
* table就是HashMap 的一组数组,类型是Node[]
* if语句表示如果当前table是null或者大小=0
* 就是第一次扩容,到16个空间,
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length;
* (1)根据key,得到hash 去计算该 key 应该存放到table表的哪个索引位置
* 并且把这个位置对象,赋给 p
* (2)判断p是否为null
* (2.1)如果p为null,表示还没有存放元素,就创建一个Node(key="java",value=PRESENT)
* (2.2)就放在该位置tab[i] = newNode(hash,key,value,null)
if ((p = tab[i = (n - 1) & hash]) == null)
tab[i] = newNode(hash, key, value, null);
else {
Node e; K k;
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
e = p;
else if (p instanceof TreeNode)
e = ((TreeNode)p).putTreeVal(this, tab, hash, key, value);
else {
for (int binCount = 0; ; ++binCount) {
if ((e = p.next) == null) {
p.next = newNode(hash, key, value, null);
if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
treeifyBin(tab, hash);
break;
}
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
break;
p = e;
}
}
if (e != null) { // existing mapping for key
V oldValue = e.value;
if (!onlyIfAbsent || oldValue == null)
e.value = value;
afterNodeAccess(e);
return oldValue;
}
}
++modCount;
* 如果size>限额,就扩容
* ⭐size就是我们每加入一个节点Node(K,V,hash,next)
* 也就是当我们给hashset增加一个元素时候,table大小也会增加
if (++size > threshold)
resize();
afterNodeInsertion(evict);
return null;
}
* */
hashSet.add("java");
}
}
后续学完红黑树再回来分析代码
LinkedHashSet 是 HashSet的子类
Linked是链表的意思
LinkedHashSet底层是LinkedHashMap,底层维护了一个数组+链表+双向链表
LinkedHashSet根据元素的hashCode值来决定元素的存储位置(数组索引),同时使用链表维护元素的次序(图),这使得元素看起来是以插入顺序保存的
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kGpauHts-1678672774062)(C:\Users\ADMIN\AppData\Roaming\Typora\typora-user-images\image-20220922161705104.png)]
LinkedHashSet不允许添加重复元素。
HashMap$Node[]
存放的元素/数据是LinkedHashMap$Entry
public class TreeSet_ {
public static void main(String[] args) {
// TreeSet treeSet = new TreeSet();
//添加数据
//1.当我们使用无参构造器,创建TreeSet时,元素是无序的
//2.当希望添加的元素,按照字符串大小来排序
//3.使用TreeSet提供的一个构造器,可以传入一个比较器接口(匿名内部类)
// 并指定排序规则
TreeSet treeSet = new TreeSet(new Comparator() {
@Override
public int compare(Object o1, Object o2) {
return ((String) o1).compareTo((String) o2);
}
});
//源码解读
//1.构造器把传入的比较器对象,赋给了TreeSet的底层TreeMap的属性this.comparator
//
treeSet.add("jack");
treeSet.add("tom");
treeSet.add("sp");
treeSet.add("a");
System.out.println("treeSet" + treeSet);//treeSet[a, jack, sp, tom] 是无序的
}
}
Hashset和hashmap的区别
注意:这里将的是JDK8的Map接口特点
Map与Collection并列存在。用于保存具有映射关系的数据:Key-Value
Map中的key和value可以是任何引用类型的数据,会封装到HashMap$Node对象中
Map中的key不允许重复,原因和HashSet一样,通过Hash检验,前面分析过源码
Map中的value可以重复
Map的Key可以为null,value也可以为null
常用String类作为Map的key
key和value之间存在单向一对一关系,即通过指定的key总能找到对应的value
Map存放数据的Key-Value示意图,一堆k-v是放在一个Node中的,又因为Node实现了Entry接口,有一些书上也说 一对k-v 就是一个Entry
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oCrRUMEq-1678672774063)(C:\Users\ADMIN\AppData\Roaming\Typora\typora-user-images\image-20220925111954226.png)]
public class Map_ {
public static void main(String[] args) {
//Map接口实现类的特点
//1.Map与Collection并列存在。用于保存具有映射关系的数据:Key-Value(双列元素)
//2.Map中的key和value可以是任何引用类型的数据,会封装到HashMap$Node对象中
//3.Map中的key不允许重复,原因和HashSet一样,通过Hash检验,前面分析过源码
//4.Map中的value可以重复
Map map = new HashMap();
map.put("no1","邓圣君");//k-v
map.put("no2","张无忌");//k-v
map.put("no1","张三丰");//当有相同的k时,就进行v的替换
map.put("no3","张三丰");//v可以重复
System.out.println(map);//{no2=张无忌, no1=邓圣君} 可看出无序输出
System.out.println(map.get("no1"));//张三丰
}
}
public class MapSource {
public static void main(String[] args) {
Map map = new HashMap();
map.put("no1","dsj");
map.put("no2","张无忌");
//1.k-v 最后是HashMap$Node node = newNode(hash,key,value,null)
//2.k-v 为了方便程序员的遍历,还会 创建 EntrySet 集合(只有collection实现了迭代器,所以需要转换成set) ,该集合存放的元素的类型是Entry
// 而一个Entry对象就有k,v EntrySet>
//3.在entrySet中,定义的类型是Map.Entry,但是实际上存放的还是HashMap$Node
// 这时因为 static class NOde implements Map.Entry
//4.当把 HashMap$Node 对象 存放到 entrySet 就方便我们的遍历,因为 Map.Entry 提供了重要方法
// K getKey(); V getValue();
Set set = map.entrySet();
System.out.println(set);
for (Object obj : set) {
// System.out.println(obj.getClass());//HashMap$Node
//为了从HashMap$Node取出k-v
//1.先做一个向下转型
Map.Entry entry = (Map.Entry)obj;
System.out.println(entry.getKey() + "-" + entry.getValue());
}
//将k-v键值对分别转换成集合
Set set1 = map.keySet();
Collection values = map.values();
}
}
Map接口遍历方法
public class MapFor {
public static void main(String[] args) {
Map map = new HashMap();
map.put("邓超","孙俪");
map.put("王宝强","马蓉");
map.put("宋","马蓉");
map.put("刘令博",null);
map.put(null,"刘亦菲");
map.put("鹿晗","关晓彤");
//第一组:先取出所有Key,通过Key取出对应的value
Set keyset = map.keySet();
//(1)增强for
System.out.println("---增强for---");
for (Object key : keyset) {
System.out.println(key + "-" + map.get(key));
}
//(2)迭代器
System.out.println("---迭代器---");
Iterator iterator = keyset.iterator();
System.out.println(iterator);
while (iterator.hasNext()) {
Object next = iterator.next();
System.out.println(next + "-" + map.get(next));
}
//第二组,把所有的values取出
Collection values = map.values();
//这里可以使用Collections使用的遍历方法
//(1)增强for
System.out.println("---取出所有的value 增强for---");
for (Object value : values) {
System.out.println(value);
}
//(2)迭代器
System.out.println("---取出所有value 迭代器---");
Iterator iterator1 = values.iterator();
while (iterator1.hasNext()) {
Object next = iterator1.next();
System.out.println(next);
}
//第三组:通过EntrySet 来获取k-v
//⭐Entry是接口,Nodes实现,但是Node访问权限为默认,我们无法引用,因此使用Entry类型
System.out.println("---使用EntrySet的for增强(第三种)---");
Set entrySet = map.entrySet();//EntrySet>
//(1)增强for
for (Object entry : entrySet) {
//将entry转成Map.Entry
Map.Entry m = (Map.Entry)entry;
System.out.println(m.getKey() + "-" +m.getValue());
}
//(2)迭代器
Iterator iterator2 = entrySet.iterator();
System.out.println("---使用EntrySet的迭代器(第三种)---");
while (iterator2.hasNext()) {
Map.Entry entry = (Map.Entry)iterator2.next();
System.out.println(entry.getKey() + "-" +entry.getValue());
}
}
}
##HashMap底层机制
HashMap类中的Node
每个数组的位置就是一个哈希值,如果两个值哈希值一样,就会占用一个位置,他们就成了一个链表
Java8优化了这个地方,如果相同哈希值,链表的长度超过8,就从链表转换成红黑树
Node类是HashMap的一个静态内部类,实现了 Map.Entry
二、数据结构
HashMap内部结构是数组(Node[] table)和链表结合组成的复合结构,数组被分成一个个桶(bucket),通过哈希值决定键值对在这个数组的寻址;哈希值相同的键值对,则以链表形式存储。需注意的是:链表大小超过阈值(TREEIFY_THRESHOLD = 8)时,链表就会被改造成树形结构。 下面示意图为一个简单的HashMap示意图:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Bt1Osy6P-1678672774064)(C:\Users\ADMIN\AppData\Roaming\Typora\typora-user-images\image-20220926105014312.png)]
存放的元素是键值对:即k-v
Hashtable的键和值都不能为null
Hashtable使用方法基本和HashMap一样
Hashtable是线程安全的(synchronized),hashMap是线程不安全的
简单看下底层结构
public class HashTableExercise {
public static void main(String[] args) {
Hashtable table = new Hashtable();//ok
table.put("john",100);//ok
table.put(null,100);//异常
table.put("john",null);//异常
table.put("lucy",100);//ok
table.put("lic",100);//ok
table.put("lic",88);//替换
table.put("hello1",1);
table.put("hello2",1);
table.put("hello3",1);
table.put("hello4",1);
table.put("hello5",1);
table.put("hello6",1);
System.out.println(table);
//1.底层有数组Hashtable$Entry[] 初始化大小为 11
//2.临界值 threshold = 8(11 * 0.75)
//3.扩容:按照自己的扩容机制来进行
//4.执行方法addEntry(hash,key,value,index);添加K-V 封装Entry
//5.当if(count>=threshold)满足时,就进行扩容
//6.按照int newCapacity = (oldCapacity << 1) + 1;的规则进行扩容
}
}
Propertier类继承自Hashtable类并且实现了Map接口也是使用一种键值对K-V的形式来保存数据
他的使用特点和Hashtable类似
Properties还可以用于从xxx.properties文件中,加载数据到Properties类对象并进行读取和修改
说明:工作后 xxx.properties文件通常作为配置文件,这个知识点在IO流举例,有兴趣可以先看文章
https://www.cnblogs.com/xudong-bupt/p/3758136.html
在开发中选择说明集合实现类,主要取决于业务特点,然后根据集合实现类特征进行选择,分析如下:
先判断存储的类型(一组对象[单列]或一组键值对[双列])
一组对象[单列]:Collection接口
允许重复:List
增删多:LinkedList[底层维护了一个双向链表]
查改多:ArrayList[地城维护Object类型的可变数组]、Vector[线程安全,效率太低面临被淘汰的威胁]
不允许重复:Set
无序:HashSet[底层是HashMap,维护一个哈希表 即(数组+链表+红黑树)]
排序:TreeSet
插入和取出顺序一致:LinkedHashSet[维护数组+链表+双向链表]
一组键值对[双列]:Map接口
键无序:HashMap[底层是:哈希表 jdk7:数组+链表,jdk8:数组+链表+红黑树]、Hashtable[线程安全]
键排序:TreeMap
键插入和取出顺序一致:LinkedHashMap
读取文件:Properties
Collections工具类介绍
排序操作
查找、替换
进程
线程
其它
并发:同一时刻,多个任务交替执行,造成一种“貌似同时”的错觉,简单的说,单核cpu实现的多任务就是并发
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-26J2DXaz-1678672774065)(C:\Users\ADMIN\AppData\Roaming\Typora\typora-user-images\image-20221018191239074.png)]
并行:同一时刻,多个任务同时执行。多喝cpu可以实现并行。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rPeMs1VX-1678672774066)(C:\Users\ADMIN\AppData\Roaming\Typora\typora-user-images\image-20221018191254163.png)]
创建线程的两种方式
在java中线程来使用有两种方法
package com.threaduse;
/*
* @author 远空_
* @version 1.0
* */
public class Thread01 {
public static void main(String[] args) {
Cat cat = new Cat();
cat.start();
}
}
//1.当一个类继承了Thread类,该类就可以当作线程使用
//2.我们会重写run方法,写上自己的业务代码
//3.run Thread类实现了Runnable接口的run方法
class Cat extends Thread{
int times = 0;
public void run(){
//Java中实现真正的多线程是start中的start()方法,run知识一个普通的方法,多线程高并发专题剖析
while(true){
System.out.println("喵喵"+(++times));
try {
Thread.sleep(1000);
}catch (InterruptedException e) {
e.printStackTrace();
}
if(times==10){
break;
}
}
}
}
使用JConsole监控线程执行情况,并画出示意图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Wqj8tGd9-1678672774067)(C:\Users\ADMIN\AppData\Roaming\Typora\typora-user-images\image-20221019094537881.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zX5VCbdV-1678672774069)(C:\Users\ADMIN\AppData\Roaming\Typora\typora-user-images\image-20230103000128684.png)]
package com.threaduse;
/*
* @author 远空_
* @version 1.0
* */
public class Thread01 {
public static void main(String[] args) throws InterruptedException {
Cat cat = new Cat();
cat.start();//启动子线程thread0
//当main线程启动一个子线程Thread-0,主线程不会阻塞,会继续执行
//这时,主线程和子线程是交替执行的
System.out.println("主线程继续执行"+Thread.currentThread().getName());
for (int i = 0; i < 60; i++) {
System.out.println("主线程 i="+i);
//让主线程休眠
Thread.sleep(1000);
}
}
}
//1.当一个类继承了Thread类,该类就可以当作线程使用
//2.我们会重写run方法,写上自己的业务代码
//3.run Thread类实现了Runnable接口的run方法
class Cat extends Thread{
int times = 0;
public void run(){
//Java中实现真正的多线程是start中的start()方法,run知识一个普通的方法,多线程高并发专题剖析
while(true){
System.out.println("喵喵"+(++times)+" 线程名:"+Thread.currentThread().getName());
try {
Thread.sleep(1000);
}catch (InterruptedException e) {
e.printStackTrace();
}
if(times==120){
break;
}
}
}
}
源码解读
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-teMDQ3az-1678672774070)(C:\Users\ADMIN\AppData\Roaming\Typora\typora-user-images\image-20221019095609583.png)]
//源码解读
/*
(1)public synchronized void start() {
start0()
}
//start0()是本地方法,是JVM调用,底层是c/c++实现
//真正实现多线程的效果,是start0(),而不是run
(2)private native void start0();
* */
说明:
使用:
package com.threaduse;
/*
* @author 远空_
* @version 1.0
* 通过实现Runnable来开发线程
* */
public class Thread02 {
public static void main(String[] args) {
Dog dog = new Dog();
//dog.start();这里不能调用start
//因此我们创建Thread对象,把dog对象(实现Runnable),放入Thread对象
Thread thread = new Thread(dog);
thread.start();
}
}
class Dog implements Runnable{
int count = 0;
@Override
public void run() {
while(true){
System.out.println("小狗汪汪叫~"+(++count)+Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
补充:
这里底层使用了设计模式[代理模式-静态代理]
//模拟一个Thread架构
class proxy implements Runnable{//你可以把Proxy类当作ThreadProxy
private Runnable target = null;
@Override
public void run() {
if(target != null){
target.run();
}
}
public proxy(Runnable target) {
this.target = target;
}
public void start(){
start0();
}
public void start0(){
}
}
基本说明
代码演示
package com.threaduse;
/*
* @author 远空_
* @version 1.0
* */
public class ThreadExit_ {
public static void main(String[] args) throws InterruptedException {
T t = new T();
t.start();
//如果希望主线程去控制子线程的终止,必须可以修改loop变量
//让t1退出run放到,从而终止t1线程->通知方式
//让主线程休眠10秒后终止子线程
Thread.sleep(5000);
t.setLoop(false);
}
}
class T extends Thread{
private int count = 0;
//设置一个控制变量
private boolean loop = true;
@Override
public void run() {
while (loop){
try {
Thread.sleep(50);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("hello"+(++count));
}
}
public void setLoop(boolean loop) {
this.loop = loop;
}
}
第一组注意细节
package com.threaduse.method;
/*
* @author 远空_
* @version 1.0
* */
public class ThreadMethod01 {
public static void main(String[] args) throws InterruptedException {
//测试相关的方法
T t = new T();
t.setName("dsj");
t.setPriority(Thread.MIN_PRIORITY);
t.start();
//主线程输出打印5个hi,然后我就中断 子线程的休眠
for (int i = 0; i < 5; i++) {
Thread.sleep(1000);
System.out.println("hi "+i);
}
System.out.println(t.getName()+" 优先级:"+t.getPriority());
t.interrupt();//当执行到这里,就会中断t线程的休眠
Thread.sleep(1000);
System.out.println("主线程结束");
}
}
class T extends Thread{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
try {
Thread.sleep(50);
} catch (InterruptedException e) {
}
System.out.println(Thread.currentThread().getName()+" 吃包子~~~~"+i);
}
try {
System.out.println(Thread.currentThread().getName()+" 休眠中~~~~");
Thread.sleep(20000);
} catch (InterruptedException e) {
//当该线程执行到一个interrupt方法时,就会catch一个异常,可以加入自己的业务代码
//InterruptException是捕获到一个中断异常
System.out.println(Thread.currentThread().getName()+"被interrupt了");
}
}
}
第二组
yield:线程的礼让。让出cpu,让其它线程执行,但礼让的时间不确定,所以也不一定礼让成功
join:线程的插队。插队的线程一旦插队成功,则肯定先执行完插入的线程所有的任务
package com.threaduse.method;
import com.threaduse.Thread02;
/*
* @author 远空_
* @version 1.0
* */
public class ThreadMethod02 {
public static void main(String[] args) {
T1 t1 = new T1();
t1.setName("T1");
t1.start();
for (int i = 0; i < 20; i++) {
System.out.println(Thread.currentThread().getName()+" 在吃第"+i+"个包子");
if(i==4){
Thread.yield();//礼让
try {
System.out.println("让子线程插队.");
t1.join();//插队
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
class T1 extends Thread{
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println(Thread.currentThread().getName()+" 在吃第"+i+"个包子");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
用户线程:也叫工作线程,当线程的任务执行完或以通知方式结束
myDaemonThread.setDaemon(true);
守护线程:一般是为工作线程服务的,当所有的用户线程结束,守护线程自动结束
常见的守护线程:垃圾回收机制
package com.threaduse.method;
/*
* @author 远空_
* @version 1.0
* */
public class ThreadMethodExercise {
public static void main(String[] args) throws InterruptedException {
MyDaemonThread myDaemonThread = new MyDaemonThread();
myDaemonThread.setDaemon(true);
myDaemonThread.start();
//如果外面希望当main线程结束后,子线程自动结束
//秩序将子进程设置为线程守护即可
for (int i = 0; i < 10; i++) {
System.out.println("宝强在辛苦的工作"+i);
Thread.sleep(1000);
}
}
}
class MyDaemonThread extends Thread{
@Override
public void run() {
for (;;){
try{
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("马蓉在外面快乐聊天,哈哈哈~~~");
}
}
}
JDK钟用Thread.State枚举表示了线程的几种状态
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cjNBBPkQ-1678672774071)(C:\Users\ADMIN\AppData\Roaming\Typora\typora-user-images\image-20221020101044087.png)]
线程状态转换图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mPv9dtdu-1678672774072)(C:\Users\ADMIN\AppData\Roaming\Typora\typora-user-images\image-20221020101347390.png)]
简介
同步具体方法-Synchronized
同步代码块
synchronized(对象){//得到对象的锁,才能操作同步代码
//需要被同步代码;
}
synchronized还可以放在方法声明中,表示整个方法-为同步方法
public synchronized void m(String name){
//需要被同步的代码
}
如何理解:
就好像某小伙伴上厕所前先把门关上(上锁),完事后再出来(解锁),那么其它小伙伴就可在使用测试了,如图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aeh0LK0P-1678672774073)(C:\Users\ADMIN\AppData\Roaming\Typora\typora-user-images\image-20221020230600013.png)]
使用synchronized解决售票问题
基本介绍
注意细节
当静态方法同步时,锁为当前类本身
synchronized(类名.class){//得到对象的锁,才能操作同步代码
//需要被同步代码;
}
同步方法(非静态)的锁可以是this,也可以是其它对象(要求是同一个对象)
每个对象都有一把锁,通过别的对象也可以实现同步访问(不一定锁本对象)
实现的落地步骤:
不使用继承Thread的原因:其为一个对象一个线程,无需同步(或者说通过静态static属性锁定一个对象,也可以实现多线程上锁)
基本介绍
多个线程都占用了对方的锁资源,但不肯相让,导致了死锁,在编程是一定要避免死锁的发生
应用案例
妈妈:你先完成作业,才让你玩手机
小明:你先让我玩手机,我才完成作业
package com.synchronized_;
/*
* @author 远空_
* @version 1.0
死锁模拟
* */
public class DeadLock_ {
public static void main(String[] args) {
DeadLockDemo A = new DeadLockDemo(true);
DeadLockDemo B = new DeadLockDemo(false);
A.start();
B.start();
}
}
class DeadLockDemo extends Thread{
static Object o1 = new Object();
static Object o2 = new Object();
boolean flag;
public DeadLockDemo(boolean flag) {
this.flag = flag;
}
@Override
public void run() {
//1.如果flag为T,线程就会先得到/持有o1对象锁,然后尝试获取o2对象锁
//2.如果线程A得不到o2对象锁,就会进行Blocked
//3.如果flag为F,线程B就会先得到/持有o2对象锁,然后就回去获取o1对象锁
//4.如果线程B得不到o1对象锁,就会blocked
if(flag){
synchronized (o1){//对象互斥锁,下面就是同步代码
System.out.println(Thread.currentThread().getName()+"进入了1");
synchronized (o2){//这里获得li对象的监视权
System.out.println(Thread.currentThread().getName()+"进入了2");
}
}
}else {
synchronized (o2){
System.out.println(Thread.currentThread().getName()+"进入了3");
synchronized (o1){
System.out.println(Thread.currentThread().getName()+"进入了4");
}
}
}
}
}
下面操作会释放锁
当线程的同步方法、同步代码块执行结束
案例:上厕所,完事出来
当前线程在同步代码块、同步方法中遇到break、return
案例:没有正常的完事,经理叫他修改bug,不得已出来
当前线程在同步代码块、同步方法出现了未处理的ERROR或Exception,导致异常结束
案例:没有正常完事,发现没带纸巾,不得已出来
当前线程在同步到代码块、同步方法中执行了线程对象的wait()方法,当钱线程暂停,并释放锁。
案例:没有正常完事,觉得需要酝酿一下,所以出来等会再进去
下面操作不会释放锁
线程执行同步代码块或同步方法时候,程序调用Thread.sleep()、Thread.yield()方法暂停当前线程的执行,不会释放锁
案例:上厕所,太困了,在坑位上眯了一会
线程执行同步代码块时,其它线程调用了该线程的suspend()方法讲该线程挂起,该线程不会释放锁
提示:应尽量避免实验suspend和re’sume方法来控制线程,方法不再推荐使用
文件,对我们并不模式,文件是保存数据的地方,比如大家经常使用的word文档,txt文件,excel文件…都是文件。它既可以保存一张图片,也可以保持视频,声音…
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-m6Eusq6E-1678672774074)(C:\Users\ADMIN\AppData\Roaming\Typora\typora-user-images\image-20221204161231602.png)]
文件再程序中是以流的形式来操作的
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UeQHumhi-1678672774075)(C:\Users\ADMIN\AppData\Roaming\Typora\typora-user-images\image-20221204161438960.png)]
流:数据再数据源(文件)和程序(内存)之间经理的路径
输入流:数据从数据源(文件)到程序(内存)的路径
输出流:数据从程序(Neicun)到数据源(文件)的路径
相关方法
new File(String pathname)//根据路径构建一个File对象
new File(File parent,String child)//根据文件对象目录+子路径构建
new File(String parent,String child)//根据父目录+子路径构建
createNewFile() //创建新文件
//File对象通常是文件路径
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-62AFGbTd-1678672774076)(C:\Users\ADMIN\AppData\Roaming\Typora\typora-user-images\image-20221204170101347.png)][外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MaC6qIIB-1678672774077)(C:\Users\ADMIN\AppData\Roaming\Typora\typora-user-images\image-20221204170114299.png)]
//先创建文件对象
File file = new File("e:\\news1.txt");
//调用相应的方法,得到对应信息
System.out.println("文件名字="+file.getName());
System.out.println("文件绝对路径"+file.getAbsoluteFile());
System.out.println("文件父级目录"+file.getParent());
System.out.println("文件大小(字节)"+file.length());
System.out.println("文件是否存在"+file.exists());
System.out.println("是不是一个文件"+file.isFile());
System.out.println("是不是一个目录"+file.isDirectory());
mkdir创建一级目录
//值得注意的是,在java中,目录也会被当作文件
String filePath = "e:\\a";
File file = new File(filePath);
if(file.exists()){
System.out.println("文件存在");
}else {
if (file.mkdir()){
System.out.println("创建成功");
}else {
System.out.println("创建失败");
}
}
mkdirs创建多级目录
//值得注意的是,在java中,目录也会被当作文件
String filePath = "e:\\a\\b\\c";
File file = new File(filePath);
if(file.exists()){
System.out.println("文件存在");
}else {
if (file.mkdirs()){
System.out.println("创建成功");
}else {
System.out.println("创建失败");
}
}
delete删除空目录或文件
//值得注意的是,在java中,目录也会被当作文件
String filePath = "e:\\news1.txt";
File file = new File(filePath);
if(file.exists()){
if(file.delete()){
System.out.println("删除成功");
}else {
System.out.println("删除失败");
}
}else {
System.out.println("文件不存在");
}
I/O是Input/Output的缩写,I/O技术是非常实用的技术,用于处理数据传输。
如读写文件,网络通讯等。
Java程序中,对于数据的输入/输出操作以“流(stream)”的方式进行。
java.io包下提供了各种“流”类和接口,用以获取不同种类的数据,并通过方法输入或输出数据。
输入input:读取外部数据(磁盘、光盘等存储设备的数据)到程序(内存)中。
输出output:将程序(内存)数据输出到磁盘、光盘等存储设备中
package inputstream_;
import org.junit.jupiter.api.Test;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
/*
* @author 远空_
* @version 1.0
* */
public class FileInputStream_ {
public static void main(String[] args) {
}
@Test
public void readFile01(){
String filePath = "e:\\hello.txt";
int readData = 0;
FileInputStream fileInputStream = null;
try {
//创建FileInputStream
fileInputStream = new FileInputStream(filePath);
//从该输入流读取一个字节的数据。如果没有输入可用,此方法将阻止。
//如果返回-1,表示读取完毕
while((readData = fileInputStream.read()) != -1){
System.out.print((char)readData);//转成char显示
//一个汉字三个字节,而这只会读入一个字节,因此会造成乱码
}
} catch (FileNotFoundException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
//关闭文件流,释放资源
try {
fileInputStream.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
/*
* 使用read(byte[] b)进行读取文件
* */
@Test
public void readFile02(){
String filePath = "e:\\hello.txt";
int readData = 0;
byte[] buf = new byte[8];//一次读取8个字节
int readLen = 0;
FileInputStream fileInputStream = null;
try {
//创建FileInputStream
fileInputStream = new FileInputStream(filePath);
//从该输入流读取最多b.length字节的数据到字节数组。如果没有输入可用,此方法将阻止。
//如果返回-1,表示读取完毕
//如果读取正常,返回实际读取的字节数
while((readLen = fileInputStream.read(buf)) != -1){
System.out.print(new String(buf,0,readLen));//转成char显示
//一个汉字三个字节,而这只会读入一个字节,因此会造成乱码
}
} catch (FileNotFoundException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
//关闭文件流,释放资源
try {
fileInputStream.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
public class FileOutputStream_ {
/*演示使用FileOutputStream将数据写到文件中,
* 如果该文件不存在,则创建该文件
* */
@Test
public void writeFile(){
//创建对象
String filePath = "e:\\a.txt";
FileOutputStream fileOutputStream = null;
try {
//得到FileOutputStream对象
//说明:
//1.new FileOutputStream(FilePath)创建,当写入内容时,会覆盖原先内容
//2.new FileOutputStream(FilePath,true)创建,写入内容时,不会覆盖,在最后追加内容
fileOutputStream = new FileOutputStream(filePath,true);
//写入一个字节
fileOutputStream.write('H');//
//写入字符串
//str.getBytes()可用把字符串->字节数组
String str = "hello,world";
fileOutputStream.write(str.getBytes());
//write(byte[] b,int off,int len)将len字节从位于偏移量off的指定字节数组写入此文件输出流
fileOutputStream.write(str.getBytes(),0,str.length());
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
/*
* @author 远空_
* @version 1.0
* */
public class FileCopy {
public static void main(String[] args) {
//完成文件拷贝,将e:\\head.jpg
FileInputStream fileInputStream = null;
FileOutputStream fileOutputStream = null;
String sourcePath = "E:\\head.jpg";
String newPath = "D:\\head.jpg";
try {
fileInputStream = new FileInputStream(sourcePath);
fileOutputStream = new FileOutputStream(newPath);
//定义一个字节数组,提高读取效率
byte[] buf = new byte[512];
int readLen = 0;
while ((readLen = fileInputStream.read(buf))!=-1){
//读取之后,就写入到文件,通过fileOutputStream
//即一遍读一边写
fileOutputStream.write(buf,0,readLen);
}
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
//关闭输入流和输出流
try {
if(fileOutputStream!=null){
fileOutputStream.close();
}
if (fileInputStream != null){
fileInputStream.close();
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
FileReader和FileWriter是字符流,即按照字符来操作io
FileReader相关方法:
相关API:
public class FileReader_ {
public static void main(String[] args) {
String filePath = "e:\\story.txt";
FileReader fileReader = null;
int data = 0;
//1.创建 FILEreader对象
try {
fileReader = new FileReader(filePath);
//循环读取使用read
while((data=fileReader.read())!=-1){
System.out.print((char) data);
}
} catch (IOException e) {
throw new RuntimeException(e);
}finally {
if(filePath!=null){
try {
fileReader.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
@Test
public void readFile02(){
String filePath = "e:\\story.txt";
FileReader fileReader = null;
char[] buf = new char[8];
int readLen = 0;
//1.创建 FILEreader对象
try {
fileReader = new FileReader(filePath);
//循环读取使用read
while((readLen=fileReader.read(buf))!=-1){
System.out.print(new String(buf,0,readLen));
}
} catch (IOException e) {
throw new RuntimeException(e);
}finally {
if(filePath!=null){
try {
fileReader.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
}
FileWriter常用方法
相关API:String类:toCharArray:将String转换成char[]
注意:FileWriter使用后,必须要关闭(close)或刷新(flush),否则写入不到指定的文件!(因为还在内存,并没有保存到外存中)
节点可以从一个特定的数据源读写数据,如:FileReader、FileWriter[源码]
处理流(也叫包装流)是“连接”在已存在的流(节点流或处理流)之上,为程序提高更为强大的读写功能,如BufferedReader、BufferedWriter[源码]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LFE9HEIq-1678672774078)(C:\Users\ADMIN\AppData\Roaming\Typora\typora-user-images\image-20221220183411824.png)]
处理流的功能主要体现在下面两个方面:
package reader_;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
/*
* @author 远空_
* @version 1.0
* */
public class BufferedReader_ {
public static void main(String[] args) throws IOException {
String filePath = "e:\\note.txt";
//创建BufferedReader
BufferedReader bufferedReader = new BufferedReader(new FileReader(filePath));
//读取
String line;//按行读取效率高
//说明
//1.bufferedReader.readLine()是按行读取
//2.当返回null时,表示文件读取完毕
while ((line = bufferedReader.readLine())!=null){
System.out.println(line);
}
//关闭流,这里注意,只需要关闭BufferedReader,因为底层会去自动的去关闭节点流FileReader。
bufferedReader.close();
}
}
public class BufferedWriter_ {
public static void main(String[] args) throws IOException {
String filePath = "e:\\ok.txt";
//创建BufferWriter
BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(filePath,true));
bufferedWriter.write("hello,韩顺平教育");
bufferedWriter.newLine();//插入一个换行,与系统相关
bufferedWriter.write("hello,韩顺平教育");
bufferedWriter.newLine();//插入一个换行,与系统相关
bufferedWriter.write("hello,韩顺平教育");
//插入一个换行
//说明:关闭外层流即可
bufferedWriter.close();
}
}
public class ObjectInputStream_ {
public static void main(String[] args) {
String file = "e:\\data.dat";
try {
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(file));
Integer n1 = (Integer) objectInputStream.readInt();
Boolean n2 = (Boolean) objectInputStream.readBoolean();
System.out.println(""+n1+n2);
//读取
//解读:
//1.读取(反序列化)的顺序需要和你保存数据(序列化)的顺序一致
//2.否则会出现异常
System.out.println(objectInputStream.readChar());
System.out.println(objectInputStream.readDouble());
System.out.println(objectInputStream.readUTF());
Object o = objectInputStream.readObject();
System.out.println("运行类型"+o.getClass());
System.out.println("dog信息"+o);
objectInputStream.close();
} catch (IOException e) {
throw new RuntimeException(e);
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
}
}
public class ObjectOutputStream_ {
public static void main(String[] args) throws IOException {
//序列化后,保存的文件格式不是纯文本,而是按照他的格式来保存
String file = "e:\\data.dat";
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream(file));
//序列化数据到e:\\data.dat
objectOutputStream.writeInt(100);//int -> Integer(实现了Serializable)
objectOutputStream.writeBoolean(true);//boolean -> Boolean(实现了Serializable)
objectOutputStream.writeChar('a');//char -> Character(实现了Serializable)
objectOutputStream.writeDouble(9.5);//double -> Double(实现了Serializable)
objectOutputStream.writeUTF("邓圣君");//String(实现了Serializable)
//保存一个dog对象
objectOutputStream.writeObject(new Dog("旺财",10));
objectOutputStream.close();
System.out.println("保存完毕");
}
}
//System 类里的 public final static InputStream in = null;
//System.in的编译类型InputStream
//System.in的运行类型BufferedInputStream
//表示的是标准输入 键盘
System.out.println(System.in.getClass());
//System 类里的 public final static PrintStream out = null;
//编译类型 PrintStream
//运行类型 PrintStream
//表示标准输出 显示器
System.out.println(System.out.getClass());
应用案例
传统方法System.out.println(“”)是使用out对象将数据输出到显示器
应用案例2
传统的方法,Scanner是从标准输入键盘接受数据
先看一个文件乱码问题,引出学习转换流的必要性
使用ANSI编码保存文件后,用java默认的bufferedreader读出的数据乱码
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UDN8KCJZ-1678672774079)(C:\Users\ADMIN\AppData\Roaming\Typora\typora-user-images\image-20221221165021581.png)]
介绍
InputStreamReader:Reader的子类,可以将InputStream(字节流)包装成Reader(字符流)
OutputStreamWriter:Writer的子类,可以实现OutputStream(字节流)包装成Writer(字符流)
当处理纯文本数据时,如果使用字符流效率更高,并且可以有效解决中文问题,所以建议将字节流转换成字符流
可以在使用时指定编码格式(比如utf-8,gbk,gb2312等)
package printstream_;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.PrintStream;
/*
* @author 远空_
* @version 1.0
* */
public class PrintStream_ {
public static void main(String[] args) throws FileNotFoundException {
PrintStream out = System.out;
//在默认情况下,PrintStream输出数据的位置是 标准输出,即显示器
out.print("邓圣君,你好");
//因为print底层调用了write,所以我们可以直接使用write进行输出
try {
out.write("java".getBytes());
} catch (IOException e) {
throw new RuntimeException(e);
}
out.close();
//我们可以去修改打印流输出的位置/设备
//1.修改成 输出到 "e:\\f1.txt
//2."hello,java native!"就会输出到文件
System.setOut(new PrintStream("e:\\f1.txt"));
System.out.println("hello,java native!");
}
}
package printstream_;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
/*
* @author 远空_
* @version 1.0
* */
public class PrintWriter_ {
public static void main(String[] args) throws IOException {
PrintWriter printWriter = new PrintWriter(new FileWriter("e:\\f2.txt"));
printWriter.print("hi,evesky!");
printWriter.close();
}
}
看一个需求
如下一个配置文件 mysql.properties
ip=127.0.0.1
user=root
pwd=123456
请问编程读取ip、user和pwd的值是多少
分析
用传统方法
public class Properties01 {
public static void main(String[] args) throws IOException {
//读取mysql.properties文件,并得到ip、user、pwd
BufferedReader bufferedReader = new BufferedReader(new FileReader("src/properties_/mysql.properties"));
String line = "";
while((line = bufferedReader.readLine())!=null){
String[] split = line.split("=");
//如果我们要求指定的ip值
if ("ip".equals(split[0])){
System.out.println("获取到ip值"+split[1]);
}
System.out.println(split[0]+"值是:"+split[1]);
}
bufferedReader.close();
}
}
使用Properties类可以方便实现
基本介绍
专门用于读写配置文件的集合类
配置文件的格式:
键=值
键=值
注意:键值对不需要有空格,值不需要用引号引起来。默认类型是String
Properties常见方法
应用案例
1.使用Properties类完成对 mysal.properties 的读取
public class Properties02 {
public static void main(String[] args) throws IOException {
//使用Properties类完成对 mysal.properties 的读取
//1.创建Properties对象
Properties properties = new Properties();
//2.加载指定的文件
properties.load(new FileReader("src/properties_/mysql.properties"));
//3.吧k-v显示到控制台
properties.list(System.out);
//4.根据key值得到value
String ip = properties.getProperty("ip");
System.out.println("获取ip:"+ip);
}
}
2.使用Properties类添加key-val 到新文件 mysql2.properties 中
public class Properties03 {
public static void main(String[] args) throws IOException {
//使用Properties类添加key-val 到新文件 mysql2.properties 中
Properties properties = new Properties();
//创建
//1.如果该文件没有key就是创建
//2.如果该文件有key,就是修改
//Properties父类是Hash她不可,底层就是Hashtable核心方法
properties.setProperty("charset","utf8");
properties.setProperty("user","邓圣君");
properties.setProperty("pwd","123456");
//将k-v存储到文件中即可
properties.store(new FileOutputStream("src/properties_/mysql.properties"),"123");
System.out.println("保存配置文件成功!");
}
}
3.使用Properties类完成对 mysql.properties 的读取, 并修改某个key-val
public class Properties03 {
public static void main(String[] args) throws IOException {
//使用Properties类添加key-val 到新文件 mysql2.properties 中
Properties properties = new Properties();
//创建
//1.如果该文件没有key就是创建
//2.如果该文件有key,就是修改
//Properties父类是Hash她不可,底层就是Hashtable核心方法
properties.setProperty("charset","utf8");
properties.setProperty("user","邓圣君");
properties.setProperty("pwd","123456");
//将k-v存储到文件中即可
properties.store(new FileOutputStream("src/properties_/mysql.properties"),"123");
System.out.println("保存配置文件成功!");
}
}
##Swing是什么?
1)AWT
在早期JDK1.0发布时,Sun公司就为GUI开发提供了一套基础类库,这套类库被称为AWT(Abstract Window Toolkit),即抽象窗口工具包。AWT的起初设想就是为了统一实现不同操作系统的图像界面,但问题是,不同操作系统图形库的功能可能不一样(比如按钮,在不同系统的就表现不一样),在一个平台上存在的功能在另外一个平台上则可能不存在,为此AWT不得不通过牺牲功能来实现平台无关性。不仅如此,AWT还是一个重量级组件,使用比较麻烦,且设计出的图形界面不够美观功能也非常有限。为此,Sun公司对AWT进行改进,提出了Swing组件,提供了更加丰富的组件和功能,来满足GUI设计的一切需求。
2)Swing
Swing是一个用于开发Java应用程序用户界面的开发工具包。使用 Swing 来开发图形界面比 AWT 更加优秀,因为 Swing 是一种轻量级组件,它采用纯 Java 实现,不再依赖于本地平台的图形界面,所以可以在所有平台上保持相同的运行效果,对跨平台支持比较出色。除此之外,Swing 提供了比 AWT 更多的图形界面组件,Swing开发人员只用很少的代码就可以利用Swing丰富、灵活的功能和模块化组件来创建优雅的用户界面。
为了和 AWT 组件区分,Swing 组件在javax.swing.*包下,类名均以 J 开头,例如: JFrame、JLabel、JButton等,而在AWT中叫Frame、Label等。
1)做系统,比如医院的软件。
2)做各种小游戏,提升逼格。
3)Intellij IDEA就是java swing开发的
4)毕业设计用的挺多。
Swing容器
Swing 中容器可以分为两类:顶层容器和中间容器,容器类都是继承自 Container 类。
顶层容器:
是进行图形编程的基础,一切图形化的东西都必须包括在顶层容器中。Swing中有三种顶层容器,分别是JFrame、JDialog 和 JApplet。
中间容器:
是容器组件的一种,也可以承载其他组件,但中间容器不能独立显示,必须依附于其他的顶层容器。常见的中间容器有 JPanel、JScrollPane、JTabbedPane 和 JToolBar。
Swing布局
1)FlowLayout(流式布局)
流式布局管理器:按水平方向依次排列放置组件,排满一行,换下一行继续排列。
2)GridLayout(网格布局)
网格布局管理器:按指定行列数分隔出若干网格,每一个网格按顺序放置一个控件。
3)GridBagLayout(网格袋布局)
网格袋布局管理器:每个组件可占用一个或多个网格,可将组件垂直、水平或沿它们的基线对齐。
4)BoxLayout(箱式布局)
箱式布局管理器:它把若干组件按水平或垂直方向依次排列放置。
5)GroupLayout(分组布局)
分组布局管理器:将组件按层次分组(串行 或 并行),分别确定 组件组 在 水平 和 垂直 方向上的位置。
6)CardLayout(卡片布局)
卡片布局管理器:它将容器中的每个组件看作一张卡片,一次只能看到一张卡片,其他卡片被遮住。
7)BorderLayout(边界布局)
边界布局管理器:它把 Container 按方位分为 5 个区域(东、西、南、北、中),每个区域放置一个组件。
8)SpringLayout(弹性布局)
弹性布局管理器:通过定义组件四条边的坐标位置来实现布局。
9)null(空布局)
空布局:也叫绝对布局,通过设置组件的坐标和宽高来布置组件。
看起来有很多是不是很吓人,其实不然,我们主要掌握以下4种
1、边界布局(BorderLayout)
2、流式布局(FlowLayout)
3、网格布局(GridLayout)
4、null(空布局)
Swing常用组件
1.实例化JFrame对象,也就是创建一个窗体。
2.设置窗体的相关属性。
3.获取一个容器。
4.创建组件。
5.向容器添加组件。
6.使窗体可视。
import java.awt.Container;
import javax.swing.JButton;
import javax.swing.JFrame;
public class SwingLearn {
public static void main(String[] args) {
//实例化 JFrame
JFrame frame = new JFrame();
//设置相关属性
frame.setTitle("Swing学习");//标题
frame.setSize(300,300);//窗体大小
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//点击关闭按钮是关闭程序
frame.setLocationRelativeTo(null); //设置居中
frame.setResizable(false); //不允许修改界面大小
//获取容器
Container container = frame.getContentPane();
frame.setLayout(null);
//创建按钮
JButton jButton = new JButton("我是按钮");
jButton.setBounds(100, 100, 100, 40);
//按钮添加到容器中
container.add(jButton);
//设置显示
frame.setVisible(true);
}
}
Swing为什么发展不好?
1)运行Swing程序,用户的电脑上必须有java运行环境,这个不太现实也很不方便。
2)本身用Swing开发出来的客户端本身比较大,客户端也需安装到用户的电脑上。
3)如果遇到不同的操作系统,需要为不同的操作系统开发一套客户端。
4)B/S系统的强势崛起,因为B/S架构多用WEB网页进行开发,不需要安装客户端,在浏览器上打开,一旦代码发生变更,客户端不需要进行升级。
本程序是一个简单的坦克游戏程序,使用java语言编写,在jdk环境下运行。游戏开始时,用户通过键盘操纵坦克移动,转弯和射击,与敌人坦克进行交战,知道消灭所有敌人就可以过关。本程序包括23个类,2800多行代码,和三个gif图片。
本程序运行的主函数
思考----编程-----思考----编程
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UDEyUaZv-1678672774083)(C:\Users\ADMIN\AppData\Roaming\Typora\typora-user-images\image-20221017083251603.png)]
绘图还必须该清楚一个非常重要的概念-像素
一个像素等于多少厘米
计算机屏幕上显示的内容都是由屏幕上的每一个像素组成的。例如,计算机显示器分辨率是800x600,表示计算机屏幕上每一行都由900个点组成,共有600行,整个计算机屏幕共有480000个像素。像素是一个密度单位,而厘米是一个长度单位,两者无法比较
Graphics 类你可以理解为画笔,为我们提供了各种绘制图形的方法:
画直线drawLine(int x1,int y1,int x2,int y2)
画矩形变矿 drawRect(int x,int y,int width,int height)
画椭圆边框 drawOval(int x,int y,int width,int height)
填充矩形 fillRect(int x,int y,int width,int height)
填充椭圆 fillOval(int x,int y,int width,int height)
画图片 drawImage(Image img,int x,int y,…)
//画图片
//1.获取图片资源
// Image image = Toolkit.getDefaultToolkit().getImage(Panel.class.getResource("/bg.jpg"));
// g.drawImage(image,10,10,200,200,this);
画字符串 drawString(String str,int x,int y)
//画字符串
//设置画笔字体
g.setColor(Color.red);
g.setFont(new Font("微软雅黑",Font.BOLD,50));
//这里设置的坐标,原点在左下角
g.drawString("北京你好",10,10);
设置画笔的字体 setFont(Font font)
设置画笔的颜色 setColor(Color c)
事件处理机制-看一个问题
BallMove.java–让小球收到键盘的控制,上下左右移动
package com.event;
import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
/*
* @author 远空_
* @version 1.0
* 演示小球通过键盘控制上下左右的移动->讲解Java的事件控制
* */
public class BallMove extends JFrame{
MyPanel mp = null;
public static void main(String[] args) {
new BallMove();
}
public BallMove(){
mp = new MyPanel();
this.add(mp);
this.setSize(400,300);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.addKeyListener(mp);
this.setVisible(true);
}
}
class MyPanel extends JPanel implements KeyListener {
private int x=10,y=10;
//为了让小球可以移动,把它的坐标设置成变量
public void paint(Graphics g){
super.paint(g);
g.fillOval(x,y,20,20);//默认黑色
}
//有字符输出时,该方法就会触发
@Override
public void keyTyped(KeyEvent e) {
}
//当某个键按下,该方法触发
@Override
public void keyPressed(KeyEvent e) {
//根据用户按下的不同键,来处理小球移动
//在java中,会给每个键分配一个值
switch (e.getKeyCode()){
case KeyEvent.VK_W:
y--;
break;
case KeyEvent.VK_S:
y++;
break;
case KeyEvent.VK_A:
x--;
break;
case KeyEvent.VK_D:
x++;
break;
}
//让面板重绘
/*在以下情况paint()将会被调用:
1. 窗口最小化,再最大化
2. 窗口的大小发生变化
3. repaint函数被调用*/
this.repaint();
}
//当某个键松开,该方法触发
@Override
public void keyReleased(KeyEvent e) {
System.out.println(e.getKeyCode());
}
}
介绍
KeyLinstener接口(需要实现)
//有字符输出时,该方法就会触发
@Override
public void keyTyped(KeyEvent e) {
}
//当某个键按下,该方法触发
@Override
public void keyPressed(KeyEvent e) {
}
//当某个键松开,该方法触发
@Override
public void keyReleased(KeyEvent e) {
System.out.println(e.getKeyCode());//输出松开的按键
}
JFrame的方法
this.addKeyListener(mp);//窗口JFrame对象可以监听键盘事件,即可以监听到面板发生的键盘事件。
java事件处理是采取“委派事件模型”。当事件发生时,产生事件的对象就会把此“信息”传递给“事件的监听者”处理,这里说的“信息”实际上就是java.awt.event事件类库里某个类所创建的对象,把它称为“事件的对象”。
前面我们提到几个重要的概念事件源,事件,事件监听器我们下面来全面的介绍它们
事件源:事件源是一个产生事件的对象,比如按钮,窗口等。
事件:事件就是承载事件源状态改变时的对象,比如当键盘事件、鼠标事件、窗口事件等等,会生成一个事件对象,该对象保存着当前事件很多信息,比如KeyEvent对象有含义被按下键的Code值。java.awt.event包和javax.swing.event包中定义了各种事件类型
事件类型:查阅jjdk文档
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FlTpfE2Y-1678672774084)(C:\Users\ADMIN\AppData\Roaming\Typora\typora-user-images\image-20221017170648885.png)]
添加功能:当玩家按下j键,发射一颗子弹。
添加功能
防止敌人坦克重叠运动
记录玩家的成绩,存盘退出
记录当时敌人的坦克坐标,存盘退出
玩游戏时,可选择是开新游戏还是继续上局游戏
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FEDL8BMf-1678672774085)(C:\Users\ADMIN\AppData\Roaming\Typora\typora-user-images\image-20230103121554217.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ar0L0q4w-1678672774085)(C:\Users\ADMIN\AppData\Roaming\Typora\typora-user-images\image-20230103131804615.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2xmckCNE-1678672774086)(C:\Users\ADMIN\AppData\Roaming\Typora\typora-user-images\image-20230103194909539.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2mGLgkYz-1678672774087)(C:\Users\ADMIN\AppData\Roaming\Typora\typora-user-images\image-20230103195212890.png)]
相关方法
public class API_ {
public static void main(String[] args) throws UnknownHostException {
//获取本机的InetAddress对线
InetAddress localhost = InetAddress.getLocalHost();
System.out.println(localhost);//小新14AIR/192.168.0.6
//2.根据指定主机名获取InetAddress 对象
InetAddress host = InetAddress.getByName("小新14AIR");
System.out.println(host);//小新14AIR/192.168.0.6
//3.根据域名返回InetAddress对象,比如www.baidu.com
InetAddress host2 = InetAddress.getByName("www.baidu.com");
System.out.println(host2);//www.baidu.com/14.215.177.39
//4.通过 InetAddress对象,获取相应ip地址和域名
String ip = host2.getHostAddress();
String name = host2.getHostName();
System.out.println(ip+" "+name);
}
}
基本介绍
基本介绍
public class SocketTCP01Server {
public static void main(String[] args) throws IOException {
//细节,要求9999端口未被占用
ServerSocket serverSocket = new ServerSocket(9999);
System.out.println("等待连接,阻塞进程");
//2.当没有客户端连接999端口时候,程序阻塞,等待连接
//如果有客户端连接从,则会返回一个Socket对象,程序继续
//细节:如果ServerSocket 可以通过accept()返回多个Socket[多个客户端连接服务器的并发]
Socket socket = serverSocket.accept();
System.out.println("socket = "+socket);
//socket = Socket[addr=/192.168.0.6,port=1404,localport=9999]
//3.socket.getInputStream()读取客户端写入到数据通道的数据
InputStream inputStream = socket.getInputStream();
//4.IO读取
byte[] buf = new byte[1024];
int readLen = 0;
while ((readLen = inputStream.read(buf))!=-1){
System.out.println(new String(buf,0,readLen));
}
//5.关闭流和socket
inputStream.close();
socket.close();
}
}
public class SocketTCP01Client {
public static void main(String[] args) throws IOException {
//1.连接服务器
Socket socket = new Socket(InetAddress.getLocalHost(), 9999);
System.out.println("客户端socket="+socket);
//客户端socket=Socket[addr=小新14AIR/192.168.0.6,port=9999,localport=1404]
//2.连接上后,生成Socket,通过socket.getOutputStream()
//得到和socket关联的输出流对象
OutputStream outputStream = socket.getOutputStream();
//3.通过输出流。写入数据到数据通道
outputStream.write("hello,server".getBytes());
//4.关闭流对象和socket,必须关闭,节省资源
outputStream.close();
socket.close();
System.out.println("客户端退出");
}
}
注意:如果客户端没有发消息write(),服务端会进行等待
public class SocketTCP02Client {
public static void main(String[] args) throws IOException {
//1.连接服务器
Socket socket = new Socket(InetAddress.getLocalHost(), 9999);
System.out.println("客户端socket="+socket);
//客户端socket=Socket[addr=小新14AIR/192.168.0.6,port=9999,localport=1404]
//2.连接上后,生成Socket,通过socket.getOutputStream()
//得到和socket关联的输出流对象
OutputStream outputStream = socket.getOutputStream();
//3.通过输出流。写入数据到数据通道
outputStream.write("hello,server".getBytes());
socket.shutdownOutput();
//4.获取和socket相关联的输入流,读取数据并显示
InputStream inputStream = socket.getInputStream();
byte[] buf = new byte[1024];
int readLen = 0;
while ((readLen=inputStream.read(buf))!=-1){
System.out.println(new String(buf,0,readLen));
}
socket.shutdownInput();
//5.关闭流对象和socket,必须关闭,节省资源
outputStream.close();
socket.close();
System.out.println("客户端退出");
}
}
public class SocketTCP02Server {
public static void main(String[] args) throws IOException {
//细节,要求9999端口未被占用
ServerSocket serverSocket = new ServerSocket(9999);
System.out.println("等待连接,阻塞进程");
//2.当没有客户端连接999端口时候,程序阻塞,等待连接
//如果有客户端连接从,则会返回一个Socket对象,程序继续
//细节:如果ServerSocket 可以通过accept()返回多个Socket[多个客户端连接服务器的并发]
Socket socket = serverSocket.accept();
System.out.println("socket = "+socket);
//socket = Socket[addr=/192.168.0.6,port=1404,localport=9999]
//3.socket.getInputStream()读取客户端写入到数据通道的数据
InputStream inputStream = socket.getInputStream();
//4.IO读取
byte[] buf = new byte[1024];
int readLen = 0;
while ((readLen = inputStream.read(buf))!=-1){
System.out.println(new String(buf,0,readLen));
}
socket.shutdownInput();
//5.获取socket相关联的输出流
OutputStream outputStream = socket.getOutputStream();
outputStream.write("hello,client".getBytes());
socket.shutdownOutput();
//6.关闭流和socket
inputStream.close();
outputStream.close();
socket.close();
}
}
注意:如果使用字符流,需要手动刷新flush(),否则数据不会写入数据通道
public class SocketTCPClient {
public static void main(String[] args) throws IOException {
//1.连接服务器
Socket socket = new Socket(InetAddress.getLocalHost(), 9999);
System.out.println("客户端socket="+socket);
//客户端socket=Socket[addr=小新14AIR/192.168.0.6,port=9999,localport=1404]
//2.连接上后,生成Socket,通过socket.getOutputStream()
//得到和socket关联的输出流对象
OutputStream outputStream = socket.getOutputStream();
OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream,"utf8");
BufferedWriter bufferedWriter = new BufferedWriter(outputStreamWriter);
//3.通过输出流。写入数据到数据通道
bufferedWriter.write("hello,server");
//如果使用字符流,需要手动刷新flush(),否则数据不会写入数据通道
bufferedWriter.flush();
socket.shutdownOutput();
//4.获取和socket相关联的输入流,读取数据并显示
InputStream inputStream = socket.getInputStream();
InputStreamReader inputStreamReader = new InputStreamReader(inputStream,"utf8");
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
String s = null;
while ((s=bufferedReader.readLine())!=null){
System.out.println(s);
}
socket.shutdownInput();
//5.关闭流对象和socket,必须关闭,节省资源
bufferedReader.close();
socket.close();
System.out.println("客户端退出");
}
}
public class SocketTCPServer {
public static void main(String[] args) throws IOException {
//细节,要求9999端口未被占用
ServerSocket serverSocket = new ServerSocket(9999);
System.out.println("等待连接,阻塞进程");
//2.当没有客户端连接999端口时候,程序阻塞,等待连接
//如果有客户端连接从,则会返回一个Socket对象,程序继续
//细节:如果ServerSocket 可以通过accept()返回多个Socket[多个客户端连接服务器的并发]
Socket socket = serverSocket.accept();
System.out.println("socket = "+socket);
//socket = Socket[addr=/192.168.0.6,port=1404,localport=9999]
//3.socket.getInputStream()读取客户端写入到数据通道的数据
InputStream inputStream = socket.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));
//4.IO读取
String s = null;
while ((s=br.readLine())!=null){
System.out.println(s);
}
socket.shutdownInput();
//5.获取socket相关联的输出流
OutputStream outputStream = socket.getOutputStream();
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(outputStream));
bw.write("hello,client!");
//如果使用字符流,需要手动刷新,否则数据不会写入数据通道
bw.flush();
socket.shutdownOutput();
//6.关闭流和socket
br.close();
socket.close();
}
}
基本介绍
基本流程
//B端,服务器
package socket.udpcopy;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
/*
* @author 远空_
* @version 1.0
* */
public class B {
public static void main(String[] args) throws IOException, InterruptedException {
DatagramSocket socket = new DatagramSocket(9999);
byte[] bytes = new byte[63 * 1024];
DatagramPacket packet = new DatagramPacket(bytes,bytes.length);
socket.receive(packet);
byte[] data = packet.getData();
int len = packet.getLength();
String fileName = new String(data, 0, len);
System.out.println("客户端要求获取文件:"+fileName);
File file = new File("E:\\下载", fileName);
if(file.exists()){
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));
int readLen = 0;
DatagramPacket sendPocket = new DatagramPacket(bytes, bytes.length, InetAddress.getLocalHost(), 8888);
String fileSize = Long.toString(file.length());
System.out.println("文件大小:"+fileSize);
sendPocket.setData(fileSize.getBytes(),0,fileSize.getBytes().length);
socket.send(sendPocket);
while ((readLen = bis.read(bytes))!=-1){
sendPocket.setData(bytes,0,readLen);
socket.send(sendPocket);
Thread.sleep(4);
System.out.println("传输");
}
}else {
System.out.println("文件不存在");
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("e:\\下载\\TankWar-master.zip"));
int readLen = 0;
DatagramPacket sendPocket = new DatagramPacket(bytes, bytes.length, InetAddress.getLocalHost(), 8888);
String fileSize = Long.toString(file.length());
sendPocket.setData(fileSize.getBytes(),0,fileSize.getBytes().length);
socket.send(sendPocket);
while ((readLen = bis.read(bytes))!=-1){
sendPocket.setData(bytes,0,readLen);
socket.send(sendPocket);
Thread.sleep(4);
System.out.println("传输");
}
}
socket.close();
}
}
//A端,客户端
import java.io.*;
import java.net.*;
import java.util.Scanner;
/*
* @author 远空_
* @version 1.0
* */
public class A {
public static void main(String[] args) throws IOException {
Scanner scanner = new Scanner(System.in);
String s = scanner.next();
DatagramSocket socket = new DatagramSocket(8888);
DatagramPacket packet =
new DatagramPacket(s.getBytes(),s.getBytes().length, InetAddress.getLocalHost(), 9999);
socket.send(packet);
byte[] bytes = new byte[63 * 1024];
DatagramPacket recevicePacket = new DatagramPacket(bytes, bytes.length);
File file = new File("e:\\",s);
file.createNewFile();
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(file));
socket.receive(recevicePacket);
String fileSize = new String(recevicePacket.getData(),0,recevicePacket.getLength());
System.out.println("文件大小:"+fileSize);
while (Long.parseLong(fileSize) > file.length()){
socket.receive(recevicePacket);
byte[] data = recevicePacket.getData();
int len = recevicePacket.getLength();
bos.write(data,0,len);
bos.flush();
System.out.println("文件传输进度:"+(100*file.length()/Integer.parseInt(fileSize))+"%");
}
System.out.println("文件传输完成");
socket.close();
}
}
多用户即时通讯系统 章节
根据配置文件 re.properties 指定信息,创建对象并调用方法
classfullpath=com.hspedu.Cat
method=hi
思考: 使用现有技术,你能做的吗?
这样的需求在学习框架时特别多,即通过外部文件配置,在不修改源码情况下,来控制程序,也符合设计模式的 ocp原则(开闭原则:不修改源码,来扩展功能)
反射机制允许程序在执行期借助于ReflectionAP取得任何类的内部信息(比如成员变量,构造器,成员方法等等),并能操作对象的属性及方法。反射在设计模式和框架底层都会用到
加载完类之后,在堆中就产生了一个Class类型的对象 (一个类只有一个Class对象),这个对象包含了类的完整结构信息。通过这个对象得到类的结构。这个对象就像一面镜子,透过这个镜子看到类的结构,所以,形象的称之为:反射
p对象 --》类型 Person类
对象 cls —>类型 Class类
这个对象的类型是 Class
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HA6rsxwe-1678672774088)(C:\Users\ADMIN\AppData\Roaming\Typora\typora-user-images\image-20230107230644304.png)]
这些类在 java.lang.reflection
/*
* @author 远空_
* @version 1.0
* */
public class Reflection01 {
public static void main(String[] args) throws Exception{
Cat cat = new Cat();
cat.hi();
Properties properties = new Properties();
properties.load(new FileInputStream("src/re.properties"));
String classfullpath=properties.getProperty("classfullpath");
String methodName = properties.getProperty("method");
//使用反射机制加载
//1.加载类,返回Class类型对象
Class aClass = Class.forName(classfullpath);
//2.通过aclass获取你加载的类的实例对象
Object o = aClass.newInstance();
System.out.println("o的运行类型"+o.getClass());
//(3)通过aclass得到你加载的类 Cat 的 methodName 的方法对象
// 即:在反射中,可以把方法视为对象(万物皆对象)
Method method = aClass.getMethod(methodName);
//(4)通过method来调用方法:即通过方法对象来实现调用方法
method.invoke(o);//传统方法 对象.方法() ,反射机制 方法.invoke(对象)
//java.lang.reflect.Field: 代表类的成员变量
//得到name字段
//getField不能得到私有的属性
Field nameField = aClass.getField("age");
//传统 对象.成员变量 ,反射:成员变量对象.get(对象)
System.out.println(nameField.get(o));//0
//java.lang.reflect.Constructor: 代表类的构造方法
Constructor constructor = aClass.getConstructor();//()中指定构造函数参数类型,这里返回无参构造器
System.out.println(constructor);//public question.Cat()
Constructor constructor1 = aClass.getConstructor(String.class);//这里传入String.class就是String类的Class对象
System.out.println(constructor1);//public question.Cat(java.lang.String)
}
}
//反射缺点体现
package question;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/*
* @author 远空_
* @version 1.0
* */
public class Relection02 {
public static void main(String[] args) throws ClassNotFoundException, InvocationTargetException, IllegalAccessException, NoSuchMethodException, InstantiationException {
m1();
m2();
// 传统功夫,耗时:6
// 船新版本,耗时:1351
}
//传统方法来调用hi
public static void m1(){
Cat cat = new Cat();
long start = System.currentTimeMillis();
for (int i = 0; i < 900000000; i++) {
cat.hi();
}
long end = System.currentTimeMillis();
System.out.println("传统功夫,耗时:"+ (end - start));
}
//反射机制调用方法
public static void m2() throws ClassNotFoundException, InvocationTargetException, IllegalAccessException, NoSuchMethodException, InstantiationException {
Class<?> aClass = Class.forName("question.Cat");
Object o = aClass.newInstance();
Method hi = aClass.getMethod("hi");
long start = System.currentTimeMillis();
for (int i = 0; i < 900000000; i++) {
hi.invoke(o);
}
long end = System.currentTimeMillis();
System.out.println("船新版本,耗时:"+ (end - start));
}
}
关闭后耗时:传统功夫,耗时:4
船新版本,耗时:695
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YL3qih4d-1678672774089)(C:\Users\ADMIN\AppData\Roaming\Typora\typora-user-images\image-20230107234251092.png)]
获取类信息
Field
Method
Constructor
package class_;
import question.Cat;
/*
* @author 远空_
* @version 1.0
* */
public class GetClass_ {
public static void main(String[] args) throws ClassNotFoundException {
//1.Class.forName
Class<?> aClass = Class.forName("question.Cat");
//2.类名.class,应用场景:用于参数传递
Class<Cat> catClass = Cat.class;
System.out.println(catClass);
//3.对象.getClass(),应用场景,有对象实例
Cat cat = new Cat();
Class aClass1 = cat.getClass();
System.out.println(aClass1);
//4.通过类加载器【4种】来获取到类的Class对象
//(1)先得到类加载器 car
ClassLoader classLoader = cat.getClass().getClassLoader();
//(2)通过类加载器得到Class对象
Class<?> aClass2 = classLoader.loadClass("question.Cat");
System.out.println(aClass2);
}
}
特殊的两种:
//5.基本数据类型,按如下方式得到Class对象
Class<Integer> integerClass = int.class;
Class<Character> characterClass = char.class;
Class<Boolean> booleanClass = boolean.class;
System.out.println();
//6.基本数据类型对应的包装类,可以通过.TYPE得到Class类对象
Class<Integer> type = Integer.TYPE;
Class<Character> type1 = Character.TYPE;
System.out.println(integerClass == type);//true
基本说明
反射机制是java实现动态语言的关键,也就是通过反射实现类动态加载
类加载时机
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-D9sOf9AP-1678672774090)(C:\Users\ADMIN\AppData\Roaming\Typora\typora-user-images\image-20230108122247800.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wVcYvnVe-1678672774091)(C:\Users\ADMIN\AppData\Roaming\Typora\typora-user-images\image-20230108122714881.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OAi5a7R7-1678672774092)(C:\Users\ADMIN\AppData\Roaming\Typora\typora-user-images\image-20230108123213872.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JoWLytaA-1678672774093)(C:\Users\ADMIN\AppData\Roaming\Typora\typora-user-images\image-20230108123618675.png)]
类似逻辑地址转换物理地址
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-e8qsT5Ja-1678672774094)(C:\Users\ADMIN\AppData\Roaming\Typora\typora-user-images\image-20230108123705579.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xXLqGHJF-1678672774094)(C:\Users\ADMIN\AppData\Roaming\Typora\typora-user-images\image-20230108123904996.png)]
方式一:调用类中的public修饰的无参构造器
方式二:调用类中的指定构造器
Class类相关方法
newlnstance : 调用类中的无参构造器,获取对应类的对象
getConstructor(Class…clazz):根据参数列表,获取对应的public构造器对象
getDecalaredConstructor(Class…clazz):根据参数列表,获取对应的所有构造器对象
Constructor类相关方法
setAccessible:暴破
newlnstance(Object…obj):调用构造器
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
/*
* @author 远空_
* @version 1.0
* */
public class ReflecCreateInstance {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
//1先获取User的Class对象
Class<?> user = Class.forName("User");
//2通过public的无参构造器创建实例
Object o = user.newInstance();
System.out.println(o);
//3.通过public的有参构造器创建实例
Constructor<?> constructor = user.getConstructor(int.class);
Object o1 = constructor.newInstance(18);
System.out.println(o1);
//4.通过非public的有参构造器创建实例
Constructor<?> declaredConstructor = user.getDeclaredConstructor(int.class, String.class);
//爆破,使用反射可以访问private构造器
declaredConstructor.setAccessible(true);
Object dsj = declaredConstructor.newInstance(18, "dsj");
System.out.println(dsj);
}
}
class User{//User类
private int age;
@Override
public String toString() {
return "User{" +
"age=" + age +
", name='" + name + '\'' +
'}';
}
private String name;
public User(String name) {
this.name = name;
}
private User(int age, String name) {
this.age = age;
this.name = name;
}
public User() {
}
public User(int age) {
this.age = age;
}
}
根据属性名获取Field对象
Field f = clazz对象.getDeclaredField(属性名)
暴破 : f.setAccessible(true); //f 是Field
访问
f.set(o,值);
syso(f.get(o));
如果是静态属性,则set和get中的参数o,可以写成null
import java.lang.reflect.Field;
/*
* @author 远空_
* @version 1.0
* */
public class ReflecAccessProperty {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchFieldException {
//1.得到Student对应Class对象
Class<?> student = Class.forName("Student");
//2.创建对象
Object o = student.newInstance();
System.out.println(o.getClass());
//3.使用反射得到age属性
Field age = student.getField("age");
age.set(o,88);
System.out.println(o);//传统功夫
System.out.println(age.get(o));//由反射获取
//4.使用反射操作name属性
Field name = student.getDeclaredField("name");
name.setAccessible(true);
//name.set(o,"dsj");
name.set(null,"dsj");//因为name是static属性,因此o也可以写出null
System.out.println(name.get(null));
}
}
class Student{
public int age;
private static String name;
public Student() {
}
@Override
public String toString() {
return "Student{" +
"age=" + age +
'}';
}
}
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/*
* @author 远空_
* @version 1.0
* */
public class ReflecAccessMethod {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
Class<?> boss = Class.forName("Boss");
Object o = boss.newInstance();
//3.调用public的hi方法
Method hi = boss.getMethod("hi",String.class);
Object invoke = hi.invoke(o,"dsj");
//4.调用private static方法,私有:declared+爆破;static:可用null
Method say = boss.getDeclaredMethod("say", int.class, String.class, char.class);
say.setAccessible(true);
Object invoke1 = say.invoke(null, 100, "dsj", 'm');
}
}
class Boss{
public int age;
private static String name;
private static String say(int n,String s,char c){
return n+" "+s+" "+ c;
}
public void hi(String s){
System.out.println("hi~"+s);
}
}
JDBC为访问不同的数据库提供了统一的接口,为使用者屏蔽了细节问题
Java程序员使用JDBC,可以连接任何提供了JDBC驱动程序的数据库系统,从而完成对数据库的各种操作。
JDBC的基本原理图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rAbI4Ofe-1678672774096)(C:\Users\ADMIN\AppData\Roaming\Typora\typora-user-images\image-20230109202041985.png)]
JDBC API是一系列的接口,它统一和规范了应用程序与数据库的连接、执行SQL语句,并到得到返回结果等各类操作,相关类和接口在 java.sql与javax.sql包中
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rOs3USe1-1678672774097)(C:\Users\ADMIN\AppData\Roaming\Typora\typora-user-images\image-20230109203455138.png)]
JDBC程序编写步骤
注册驱动 - 加载Driver 类
获取连接 - 得到Connection
执行增删改查 - 发送SQL 给mysql执行
释放资源 - 关闭相关连接
import com.mysql.cj.jdbc.Driver;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;
/*
* @author 远空_
* @version 1.0
* */
public class JDBC01 {
public static void main(String[] args) throws SQLException {
// 1. 注册驱动 - 加载Driver 类
Driver driver = new Driver();
// 2. 获取连接 - 得到Connection
//解读:
//(1)jdbc:mysql:// 表示规定好的协议,通过jdbc的方式,连接mysql
//(2)localhost 主机,可以是ip地址
//(3)3306表示mysql监听的端口号
//(4)hsp_db02表示连接到mysqldb的哪个数据库
//(5)mysql的连接本质就是前面学过的socket连接
String url = "jdbc:mysql://localhost:3306/hsp_db02";
//将用户名和密码放入到Properties对象
Properties properties = new Properties();
properties.setProperty("user","root");
properties.setProperty("password","123456");
Connection connect = driver.connect(url, properties);
// 3. 执行增删改查 - 发送SQL 给mysql执行
String s = "insert into actor values(null,'刘德华','男','1970-11-11','110')";
//statement 用于执行静态sql语句并返回
Statement statement = connect.createStatement();
int rows = statement.executeUpdate(s);//如果是dml语句,返回的就是影响的行数
System.out.println(rows > 0 ? "成功":"失败");
// 4. 释放资源 - 关闭相关连接
statement.close();
connect.close();
}