Java SE Web Android
2025-09-03 18:01:24 1 举报
AI智能生成
分享,随时取消,不建议克隆,持续更新,Web更完会加入MySQL,后续还会将Kotlin Android也加入,还有harmony
作者其他创作
大纲/内容
Java SE
Java 编程基础
命名标准
基础
字符与常量
int 65~90 对应 char A~Z
int 97~122 对应 char a~z
赋值
java中一般都是将 (右边)赋与(左边)
扩展名
编辑JAVA源文件产生相应字节码文件的后缀扩展名为.class
量
常量
1.整数常量
二进制:以0b/0B开头,由0和1组成的数字序列
八进制:必须以0开头,由01234567组成的数字序列
十机制:由0123456789组成的数字序列
十六进制:以0x/0X开头,由0123456789和ABCDEF(不区分大小写)组成的数字序列
2.浮点数常量
单精度浮点数:以F/f结尾
双精度浮点数(默认):以D/d结尾
3.字符常量
用' '括起来的英文字母,数字,标点符号
'\u0000' 为空白字符
4.字符串常量
用" "括起来的一串连续字符
5.布尔常量
true和false
6.null常量
null:表示应用的对象为空
变量
数据类型的转换
自动类型转换(不需要显示声明)
同时满足:数据类型兼容,目标类型取值范围大于源类型取值范围(详见量-变量-基本数据类型)
byte→short,int,long,float,double
char是16位无符号Unicode(0~65535),byte是8位有符合(-128~127)故需显式转换
short,char→int,long,float,double
int→long,float,double
float→double
long→double
强制类型转换(显式类型转换)
两种类型不兼容 或 目标类型范围小于源类型范围
如int→byte
int num = 4
byte b = (byte) num
byte b = (byte) num
目标类型 变量 = (目标类型) 值
基本数据类型
数值类型
整数类型
byte:8位(一个字节)
short:16位(两个字节)
(默认)int:32位(四个字节)
long:64位(八个字节)
浮点类型
float:32位(四个字节)
(默认)double:64位(八个字节)
字符类型
char:16位(两个字节)
布尔类型
boolean::1位/一字节/四字节
只有true和false俩个结果
引用数据类型
数组
变量的作用域
{
变量
}
变量
}
变量所属的大括号包含的代码区域就是变量的作用域,在变量的作用域外使用变量会报错
符
标识符:即变量,用于标记内存单元
不能以数字开头,不能是JAVA关键字
运算符
赋值运算符
=:通常将等号右边的赋于左边
+=:左=左+右
a+=b → (a=a+b)不反过来的原因是因为=的存在,认为是右赋值给左(a+b=a是不成立的)
-=:减等于
*=:乘等于
/=:除等于
%=:模等于
算术运算符
(先乘除后加减)
(先乘除后加减)
+:正,加,字符串相加
-:负,减
*:乘
/:除
若内存单元为整数型,只保留整数部分,舍弃小数部分(int x =10/3→x=3)
被模数%模数:取模(求余)
运算结果的正负只与被模数有关(-5%3=-2)(5%-3=2)
++
自增(前)++n:先运算后取值
自增(后)n++:先取值后运算
--
自减(前)--n:先运算后取值
自减(后)n--:先取值后运算
比较运算符
(运算结果为布尔类型ture和false)
(运算结果为布尔类型ture和false)
==:等于
判断基本类型和引用类型。判断基本类型时,判断的是值是否相等;
判断引用类型时,判断的是地址是否相等;
判断引用类型时,判断的是地址是否相等;
equals
euqalsIgnoreCase()
忽略大小写匹配,所以网站搜索资源时不管大小写总是能匹配到作者
!=:不等于
<:小于 ;<=:小于或等于
>:大于 ;>=:大于或等于
逻辑运算符
逻辑或Ⅰ
非短路或Ⅰ:左右两边都会运算
短路或ⅠⅠ:左边为ture,则右边不进行运算(短路效率高)
逻辑与&
非短路与&:左右两边都会运算
短路与&&:左边为false,则右边不进行运算(短路效率高)
逻辑非!:取反;真变假,假变真
逻辑异或^:相同为假,不同为真
三元运算符
条件表达式?表达式1:表达式2
如果条件表达式为 true,运算后的结果是表达式 1;
如果条件表达式为 false,运算后的结果是表达式 2;
匿名对象的调用
实例方法可以不创建实例访问变量和方法
new 类名().方法
方法链式调用
见JAVA API 日期时间类
类名.静态方法().实例方法()
父类引用指向子类对象
Animal myPet = new Dog();
编译时声明类型 运行时实际类型
编译时声明类型 运行时实际类型
学习面向对象后就可以理解
new Dog()就是创建对象的关键,因为Dog类继承自Animal类
(匿名对象的调用)
new Dog()就是创建对象的关键,因为Dog类继承自Animal类
(匿名对象的调用)
该引用调用的方法是子类重写的方法(因为编译器只认声明类型)或父类原有方法
Dog d = new Dog();
Animal myPet = dog;
与上面是等价的,只是创建了一个实例,而上面的匿名对象调用在每次运行时创建的都不是重复的对象。
Animal myPet = dog;
与上面是等价的,只是创建了一个实例,而上面的匿名对象调用在每次运行时创建的都不是重复的对象。
JAVA运行时只检查编译类型是否包含方法
如果包含输出的是子类的结果
不包含,编译错误
可以在父类添加空声明以指向子类
Java 语法基础
循环语句
if语句
单分支:if(条件){
执行句(可多句)
}
执行句(可多句)
}
如果条件满足(ture),则会执行,否则结束
双分支:if(条件){
执行句1
}else{
执行句2
}
执行句1
}else{
执行句2
}
如果条件满足(ture),则会执行句1,否则执行句2
多分支:if(条件1){
执行句1
}
else if(条件2){
执行句2
}
……
else{
末执行句
}
执行句1
}
else if(条件2){
执行句2
}
……
else{
末执行句
}
如果满足条件1,则执行句1,否则判断满足条件2,则执行句2
若全部条件不满足,则执行末句
若全部条件不满足,则执行末句
switch语句
switch (表达式){
case 目标值1:
执行语句1
break;
……
case 目标值n:
执行语句n
break;
default:
执行语句n+1
break;
} 表达式的值与每个case中的目标值进行匹配,如果找到了匹配的值,则执行对应case后面的语句;
如果没找到任何匹配的值,则执行default后的语句
如果没找到任何匹配的值,则执行default后的语句
1)表达式数据类型,应和case 后的常量类型一致,或者是可以自动转成可以相互比较的
类型,比如输入的是字符,而常量是int
类型,比如输入的是字符,而常量是int
2)Switch(表达式)中表达式的返回值必须是:(byte,short,int,char,enum[枚
举],String)
double c = 1.1;
Switch(c) {//错误
case 1.1://错误
System. out println("ok3");
break; }
3)case子句中的值必须是常量,而不能是变量
4)default子句是可选的,当没有匹配的case时,执行default
5)break语句用来在执行完一个case分支后使程序跳出switch语句块;如果没有写
break,程序会顺序执行到switch结尾,除非遇到break;
输入字符串时要 case "a"
for语句
单for
for(变量初始值;循环条件;变量运算){
执行句
}
执行句
}
循环以初始值→循环条件→执行句→变量运算→循环条件→执行句进行
当某次变量运算后不符合执行条件时便会结束
当某次变量运算后不符合执行条件时便会结束
1) 循环条件是返回一个布尔值的表达式;
2) for(;循环判断条件;) 中的初始值和变量运算可以写到其它地方,但是两边的分号不能省略。
3) 循环初始值可以有多条初始化语句,但要求类型一样,并且中间用逗号隔开,变量运算也可以有多条变量迭代语句,中间用逗号隔开;
计数器的使用
for嵌套
(外循环)for(i=1;i<=9;i++){
(内循环)for(){
}
System.out.println();//结果有9行
}
(内循环)for(){
}
System.out.println();//结果有9行
}
针对二维平面图形进行的循环
外循环控制行数,有几行循环几次
内循环控制每一行的输出内容
外循环控制行数,有几行循环几次
内循环控制每一行的输出内容
若左边是固定值,则内循环变量初始值为常量,循环条件为变量
若右边是固定值,则内循环变量初始值为变量,循环条件为常量
若右边是固定值,则内循环变量初始值为变量,循环条件为常量
推荐先内循环变量运算,后通过变量运算判断内循环变量和常量
再判断外循环与内循环变量的关系
再判断外循环与内循环变量的关系
九九乘法表
(总是从1开始,故初始值为常量)
(总是从1开始,故初始值为常量)
俩内循环一个控制内容输出一个控制空格输出达成特殊效果
for与if的结合使用
for一般用于循环相同的操作,if可以做到更改其中一个。
因为for方法内的都是使用局部变量,若不使用if,则要在外面重新声明变量和方法,外面的变量和方法又因为i这个循环条件的存在不能作用于for内
for(int i = 1; i≤10;i++){
if(i=5){
System.out.println("我的输出模式被更改了")
}else{
System.out.println("第"+i+"次循环");
}
}
if(i=5){
System.out.println("我的输出模式被更改了")
}else{
System.out.println("第"+i+"次循环");
}
}
for循环一般用在循环次数知道的情况下;
while循环一般用在循环次数不知道的情况下。
while语句
变量初始值;
while(循环条件){
执行句;
变量运算;
}
while(循环条件){
执行句;
变量运算;
}
循环以初始值→循环条件→执行句和变量运算→循环条件→执行句和变量运算进行
当某次变量运算后不符合执行条件时便会结束
当某次变量运算后不符合执行条件时便会结束
进制转换+方法调用
do-while语句
while是先判断后循环,而do-while是先循环后判断(do-while至少循环一次)
break和continue语句
break语句用于强制跳出循环
continue语句执行时,强制结束本轮循环,重新判断循环条件开始循环
方法与调用
方法类型
有返回值类型
public static 变量类型 变量名(参数){
命令块
return
}
命令块
return
}
必须在方法的最后添加return yl语句来返回运算数据,返回值会返回到方法调用处,可将返回值赋值给变量(int a = yl(参数))
变量类型必须与方法返回类型兼容
无返回值类型
public static void 变量名(参数){
命令块
}
public static void 变量名(参数){
命令块
}
class Student {
private String name;
private int age;
public void setName(String n){
name = n;
}
public void setAge(int a){
age = a;
}
public String getName(){
return name;
}
public int getAge(){
return age;
}
}
public class Test{
public static void main(String[] args) {
Student stu = new Student();
/*String name = stu.setName("张三");*///原来前面的类型是返回值啊
stu.setName();无返回值的方法正确调用方式
System.out.println(stu.getName());
System.out.println(stu.getAge());
}
}
private String name;
private int age;
public void setName(String n){
name = n;
}
public void setAge(int a){
age = a;
}
public String getName(){
return name;
}
public int getAge(){
return age;
}
}
public class Test{
public static void main(String[] args) {
Student stu = new Student();
System.out.println(stu.getName());
System.out.println(stu.getAge());
}
}
注意事项
方法独立:一个方法不能包含另一个方法,但一个方法内可以调用另一个方法
参数的传入
将主方法的参数传递给子方法
例如main方法下有String str
那么调用时test1(str)
那么调用时test1(str)
子方法public static void test1(String str){}
便能将str传入test1
便能将str传入test1
数组
方法重载
定义:在同一个作用域内的方法名相同但参数个数或参数类型不同的方法
参数顺序不同,但类型个数相同不是方法重载
定义
数据类型 数组名[] = new 数据类型[大小]
数据类型详见量—变量—基本数据类型
动态初始化
int[] a = new int[x] 或者 int a[] = new int[x]
没有为变量赋值时,int类变量默认值为0
静态初始化
int[] a = {元素值1,元素值2.....} 或者 int a[] = {元素值1,元素值2.....}
动静结合
int[] a = newi int[]{1,2,3}
直接为变量赋值
不能没有[],只有{}。new int{}是不行的;
[]应为空。new int[3]{1,2,3}是不行的;
[]应为空。new int[3]{1,2,3}是不行的;
索引 (即x是从0开始的)
int[] a = new int[3],a[0],a[1],a[2]
String类数组
赋值时必须用(" ")双引号,不能用单引号
String类的默认值为null
冗余的new String(sa[i]),因为sa[i]已经是字符串,不需要再次实例化
char类数组
赋值时必须用(' ')单引号,不能用双引号
char类的默认值为\u0000,即一个空字符
赋值
基本数据类型赋值
a[0]=666,a[1]=777,{1,2,3,4,5,6}对应a[0]-a[5]
地址赋值
int[] a = new int[5]
int[] b = a
int[] b = a
将a的地址赋予b
b[3]=5 → a[3]=5
赋予地址后,被赋予地址中的某个数改变,赋予地址的对应数也会改变
赋值数量
int[] c = {1,2,3,4,5,6}
int[] d = new int[c.length]
int[] d = new int[c.length]
d[]的个数与c保持一致,但数值大小不一定相等,且c与d的地址不同
数组拷贝(内容复制)
(与地址赋值的区别是俩数组地址不同)
(与地址赋值的区别是俩数组地址不同)
for 循环复制
数组copyOf(Range)复制
初始值
int,short,byte,long都初始值为0
double,float的初始值为0.0
char的初始值为\u0000,一个空白符
boolean的初始值为false
引用数据类型的初始值(如String)为null
输出
a,b,c,d
输出的是各个数组的地址
a[],b[],c[],d[]
输出的是各个变量对应的值
Arrays.toString(a,b,c,d)
依次输出每个数值的每个变量的值
练习
数组输出26个英文字母
获取最大值
求和与平均数
排序(冒泡排序)
升序,9数
第一步:第一个数与第二个数比,一大二换位,一小二不换位,一与二比,二与三比……八与九比,共8次即arr.length-1次
第二步:第一个数与第二个数比……七与八比,共7次,…………同上循环到一与二比
建立循环,外循环控制次数,八与九比到一与二比共8次(外循环八次),内循环控制对比和换位
二维数组
未初始化的二维数组的默认值都是null
定义
int[][] a 或 int a[][] 或 int[] a[]
声明int[] x,y[]; ——即创建一个一维数组x和一个二维数组y
a[]是包含于b[][]中的,故b[0]=a可将a的地址赋给b[0],但a[0]=b是会报错的,因为b包含a
动态初始化
a[行][列]
a[3][4]
a[0]
a[1]
a[2]
输出时显示的是地址
a[1]
a[2]
输出时显示的是地址
a[0][0] a[0][1] a[0][2] a[0][3]
a[1][0] a[1][1] a[1][2] a[1][3]
a[2][0] a[2][1] a[2][2] a[2][3]
可利用循环为其赋值
a[1][0] a[1][1] a[1][2] a[1][3]
a[2][0] a[2][1] a[2][2] a[2][3]
可利用循环为其赋值
a[1][2]前有6个元素
?使用方式未知
a[行][ ]
a[行][ ]
a[3][ ]
a[0]
a[1]
a[2]
输出是显示的是null(未初始化)
a[1]
a[2]
输出是显示的是null(未初始化)
a[0][0] ························
a[1][0] a[1][1]··············
a[2][0] a[2][1] a[2][2]····
a[1][0] a[1][1]··············
a[2][0] a[2][1] a[2][2]····
列的个数不确定
用于不同组得组里数的运算
3个小组,每个小组有不等的组员,每个组员的销售额不同哦们,求各个小组的总销售额
静态初始化
int[][] a = {{第0行初始值},{第1行初始值},······}
int[][] a = {{1,2},{3,4,5,6},{7,8,9}}
a[0]
a[1]
a[2]
输出时显示的是地址
a[1]
a[2]
输出时显示的是地址
1,2
3,4,5,6
7,8,9
3,4,5,6
7,8,9
动静结合
int[][] a = new int[][]{{1,2},{3,4,5,6},{7,8,9}};
其限制与定义中的动静结合类似
Java 面向对象
定义
类
变量
成员变量
类变量(静态变量)
声明为static的静态变量
口语化称为全局变量
属于类本身,所有类的实例共享
在类加载时初始化,且只初始化一次
可以通过类名访问,也可以使用对象访问
实例变量(类的属性)
非静态变量(可进行封装public,protected,default,private)
属于类的实例(对象)
在创建对象时初始化,每创建一个新对象初始化一次
必须通过对象实例访问
局部变量
(局部变量没有访问控制权限,无法被其他类访问,无法进行封装)
属于成员方法,只能在方法内使用
成员方法
和类方法(静态方法)
实例方法
对象(类的实例)
创建
实例化对象
类名 对象名 =new 类名()
↓ ↓
对象存放 → new开辟出堆内存
栈内存 指向对应 (存放实例化后的成员变量)
↓ ↓
对象存放 → new开辟出堆内存
栈内存 指向对应 (存放实例化后的成员变量)
使用
访问类的属性或方法
对象名.属性名
访问后可对类的属性进行赋值(stu.age='18')
对象名.方法名
访问后会执行方法代码运算
构造方法
(属于成员方法的一种(特殊:方法名与类名一致))
(属于成员方法的一种(特殊:方法名与类名一致))
种类
无参构造方法
默认,java中每一个类都至少有一个构造方法,
如若用户无定义,系统会自动创建一个默认的无参构造方法(如下)
如若用户无定义,系统会自动创建一个默认的无参构造方法(如下)
class Student{}=class Student{public Student(){}}
如果用户定义了一个含参构造方法,则系统不会提供无参构造方法,
此时如果用户实例化对象时无参,运行java将会报错,因为此时要调用无参构造方法。
Student stu=new Student()←无参
此时如果用户实例化对象时无参,运行java将会报错,因为此时要调用无参构造方法。
Student stu=new Student()←无参
↓
故要定义含参构造方法,建议同时定义无参构造方法
有参构造方法
构造方法的重载(构造方法的参数类型或参数个数不同)
创建对象时依据参数个数或类型调用构造方法为属性赋值
public Student(String n){
name=n;
}
Student stu1=new Student("张三")→实例化对象时会调用构造方法为属性name赋值,
作用
可以在类实例化对象时自动调用为类的属性赋值,即为成员变量赋值
构造方法属于类的一部分,所以实例化时可以访问private的类属性
权限
public(公共)
不严格
不严格
带有此权限的成员变量和成员方法在任何地方可见
protected(受保护)
同一类中
package com.packageA;
class Student1{
protected String name;
void show(){
System.out.print(name);
}
}
class Student1{
protected String name;
void show(){
System.out.print(name);
}
}
同一包中的(子)类
package com.packageA;
class Student1{
protected String name;
}
class Student1{
protected String name;
}
package com.packageA;
class Student2{
Student2 stu = new Student2();name 是 Student1 的字段,
System.out.print(stu.name);而 Student2 没有定义 name 字段
Student1 stu = new Student1();
System.out.print(stu.name);
}
}
class Student2{
System.out.print(stu.name);
}
}
不同包的子类
必须是protected成员所在类的子类
必须是protected成员所在类的子类
package com.packageA;
class Student1{
protected String name;
}
class Student1{
protected String name;
}
package com.packageB;
class Student2 extends Student1{ 确保父类内部不被破坏
Student1 stu = new Student1();跨包的子类(Student2)不能通过父类实例(stu)访问 protected 成员(name)
System.out.print(stu.name);只能通过继承直接使用该成员
public void printName() {
System.out.println(name);// ✅ 正确:直接访问继承的 protected 成员
}
class Student2 extends Student1{ 确保父类内部不被破坏
System.out.print(stu.name);
public void printName() {
System.out.println(name);// ✅ 正确:直接访问继承的 protected 成员
}
default(默认,即无修饰符)
没有修饰符!=default,
java不存在default关键字
没有修饰符!=default,
java不存在default关键字
权限
同一类中
同一包中的(类)
访问
例如String name(默认访问权限)
在访问时,利用对象调用(与方法调用有区别)
方法调用
stu.show();
变量调用
stu.name = ''张三";
stu.age = 18;
private(私有)
最严格
最严格
同一类中
带有此权限仅在类内部可见
带有此权限仅在类内部可见
外部想要访问A时,必须通过访问A所在类的
public等权限的方法,进而访问private权限的A
public等权限的方法,进而访问private权限的A
public class Student {
// private 成员变量,只能在当前类内部访问
private int age = 18;
// 类内部的方法可以直接访问 private 成员
public void printAge() {
System.out.println("年龄: " + age); // ✔️ 允许访问
}
// 提供公共方法供外部间接访问 age(封装性)
public int getAge() {
return age; // ✔️ 允许访问
}
}
// private 成员变量,只能在当前类内部访问
private int age = 18;
// 类内部的方法可以直接访问 private 成员
public void printAge() {
System.out.println("年龄: " + age); // ✔️ 允许访问
}
// 提供公共方法供外部间接访问 age(封装性)
public int getAge() {
return age; // ✔️ 允许访问
}
}
public class StudentTest {
public static void main(String[] args) {
Student student = new Student();
// ❌ 直接访问 private 成员(编译错误)
// System.out.println(student.age);
// ✔️ 通过公共方法间接访问
System.out.println("通过getAge()获取年龄: " + student.getAge());
}
}
public static void main(String[] args) {
Student student = new Student();
// ❌ 直接访问 private 成员(编译错误)
// System.out.println(student.age);
// ✔️ 通过公共方法间接访问
System.out.println("通过getAge()获取年龄: " + student.getAge());
}
}
与关键字static一样,不能修饰局部变量,局部变量只在自身作用域({局部变量}大括号内即为作用域)起作用
核心思想
类的封装
封装:利用private修饰类的属性,使其私有化。被私有化的属性只能在类中被访问(class Student{类中})
作用:迫使外界通过类中提供的方法访问属性
不私有化
年龄→被访问→-18岁
私有化
年龄→类中方法→被访问→-18岁报错(只允许0~120岁)
(if判断赋值是否通过)
(if判断赋值是否通过)
方法
在类中调用属性时,无需通过getter和settee
设置
setter:public void setAge(int age){
if(age<0){ 用于访问时赋值
System.out.print("您输入的年龄有误!");
}else{
this.age=age;
}
}
if(age<0){ 用于访问时赋值
System.out.print("您输入的年龄有误!");
}else{
this.age=age;
}
}
用于判断赋值是否通过
外界访问时,对象.setAge(赋值);访问方法
如果是访问public权限的变量,通过stu.name="张三"
获取
getter:public int getAge(){
return age
}
return age
}
外界访问这个方法,而方法在类内部,
在返回age时是在类中可以访问,return会将值返回至调用方法处
在返回age时是在类中可以访问,return会将值返回至调用方法处
外界访问时,对象.getAge();
int age = 对象.getAge,System.out.print("年龄为:“ + age)
或
System.out.print("年龄为:“ + 对象.getAge)
或
System.out.print("年龄为:“ + 对象.getAge)
类的继承
继承:class 子类 extends 父类
具有单向性,父类是独立的,子类的改变不影响父类,父类的改变会影响子类
作用:继承父类的所有非私有成员(变量和方法),可以调用父类非私有的属性和方法,可以扩充父类功能,也可以创建自己独有的属性和方法
子类无法直接继承父类的对象
必须经过对象类型的转换
特点
一个子类不能同时继承多个父类
一个父类可以被多个子类继承
可以多重继承,A→B→C
A是B和C的父类
B是C的父类,也是A的子类
C既是A的子类,也是B的子类
B是C的父类,也是A的子类
C既是A的子类,也是B的子类
详见多态
继承链:Dog→ Animal→ Object。
因此,Dog对象既是 Dog的实例,也是其所有父类(Animal、Object)的实例。
继承关系使得Dog对象天然就满足是一个Animal的条件
因此,Dog对象既是 Dog的实例,也是其所有父类(Animal、Object)的实例。
继承关系使得Dog对象天然就满足是一个Animal的条件
类的多态
对象类型的转换
向上转型
父类类型 父类对象 = 子类实例
Animal myPet = new Dog();匿名对象调用
编译时声明类型 运行时实际类型
Animal myPet = new Dog();匿名对象调用
编译时声明类型 运行时实际类型
该引用(父类对象)调用的方法是子类重写的方法(因为编译器只认声明类型)或父类原有方法
动态连接,动态调用
动态连接,动态调用
向下转型
父类类型 父类对象 = 子类实例
子类类型 子类对象 = (子类类型) 父类对象(父类引用强制转换为子类类型)
子类类型 子类对象 = (子类类型) 父类对象(父类引用强制转换为子类类型)
向下转型,将父类强制转型为子类类型后,可以利用子类对象调用子类特有方法
不能直接将父类实例强制转换为子类实例
Dog d = (Dog) new Animal(); //编译错误
Dog d = (Dog) new Animal(); //编译错误
形式
方法重载
(编译时(静态)多态)
(编译时(静态)多态)
同一个类中可以有多个同名的方法,但参数必须不同
public class MathUtils {
public int add(int a, int b) {
return a + b;
}
public double add(double a, double b) {
return a + b;
}
}
public int add(int a, int b) {
return a + b;
}
public double add(double a, double b) {
return a + b;
}
}
重载
(与参数名称无关)
(满足其一即可)
(与参数名称无关)
(满足其一即可)
参数类型不同
参数数量不同
参数顺序不同
int a,int b 和int b,int a类型相同,与名称无关,不是重载
不构成重载
仅返回值类型不同
public int add(int a, int b)
public double add(int a, int b)
public double add(int a, int b)
仅参数名不同
public int add(int a, int b)
public int add(int c, int d)
public int add(int c, int d)
仅访问修饰符不同
public int add(int a, int b)
private int add(int a, int b)
private int add(int a, int b)
方法重写(@Override)
(运行时(动态)多态)
(运行时(动态)多态)
父子类之间可以有多个同名的方法,且参数必须相同,返回值必须相同,子类的访问权限不能比父类更严格
class Animal {
void speak(){
System.out.print("动物叫……")
}
}
class Dog extends Animal {
@Override(可选注释,在编译时判断是否正确重写)
void speak(){
System.out.print(" 汪汪汪……")
}
}
void speak(){
System.out.print("动物叫……")
}
}
class Dog extends Animal {
@Override(可选注释,在编译时判断是否正确重写)
void speak(){
System.out.print(" 汪汪汪……")
}
}
关键字
class
定义类的关键字
this
(可以理解为当前类的实例对象,只是比喻)
(可以理解为当前类的实例对象,只是比喻)
public Student(String name){
name=name;(自己赋值自己,name未实例化,默认值为null)
}
Student stu1=new Student("张三")
无法确定哪个名称是当前对象的属性(对象名.属性时可以理解为成员变量成为对象的属性)
public Student(String name){
this.name=name;
}
Student stu1=new Student("张三")
调用属性:指代当
前对象(this.name可以理解为stu.name)
前对象(this.name可以理解为stu.name)
调用成员方法
this指向的是当前类中的实例的对象,一般只用于本类中调用成员方法
调用本类中的构造方法
this()→无参或含参
只能在构造方法中调用,成员方法不行
this()必须在本类构造方法第一行,并且只能出现一次,不能与super同时出现
static
super
调用父类普通属性,方法,构造方法
访问变量和方法
super.成员变量
super.成员方法(参数1,参数2……)
访问构造方法
父类
class Animal{
public Animal(String name ,int age){
}
}
class Animal{
public Animal(String name ,int age){
}
}
子类
class Dog extends Animal{
public Dog(String name ,int age ,String color){
super(name,age);//调用父类含有俩个参数的构造方法
this.setColor(color);// 利用this调用类中的setColor方法,因color为私有属性
}
class Dog extends Animal{
public Dog(String name ,int age ,String color){
super(name,age);//调用父类含有俩个参数的构造方法
this.setColor(color);// 利用this调用类中的setColor方法,因color为私有属性
}
必须位于子类构造方法的第一行,并且只能出现一次,不能与this同时出现
final
修饰的类不能有子类
方法不能被子类重写
变量(成员和局部)是常量,常量不可修改
方法不能被子类重写
变量(成员和局部)是常量,常量不可修改
修饰类
不可以被继承,即不能派生子类
修饰方法
public final void show(){}
被子类重写会报错
修饰变量
被修饰的变量为常量,常量只能在声明时被赋值一次(不能修改)
final int AGE =18;再次赋值会报错
final int AGE =18;再次赋值会报错
使用final声明变量时,要求全部的字母大写
使用public static final声明时,变量成为全局常量
public static final String NAME = ""
public static final String NAME = ""
instanceof
判断一个对象是不是某个类或接口的实例
对象 instanceof 类(或接口)
涉及类的继承
对象 instanceof 类(或接口)
涉及类的继承
Animal a1 = new Dog();向上转型父类引用指向子类对象
a1 instanceof Animal = true;
a1 instanceof Dog = true;
a1 instanceof Animal = true;
a1 instanceof Dog = true;
编译器只认声明类型,所以a1是父类对象,而Dog继承自Animal类,继承父类非私有对象,所以a1也是子类对象
(详见多态的向上转型)
(详见多态的向上转型)
new是创建实例的关键,本质是创建子类对象
a1 instanceof Animal为 true:因为 Dog继承自 Animal,满足“是一个”关系
a1 instanceof Dog为true:因为对象的实际类型是 `Dog
a1 instanceof Animal为 true:因为 Dog继承自 Animal,满足“是一个”关系
a1 instanceof Dog为true:因为对象的实际类型是 `Dog
实际对象类型是 Dog。
继承链:Dog→ Animal→ Object。
- 因此,Dog对象既是 Dog的实例,也是其所有父类(Animal、Object)的实例
继承关系使得Dog对象天然就满足是一个Animal的条件 Dog d = new Dog();
d instanceof Animal = true
其实就是判断Dog对象是否属于Animal类型
d instanceof Animal = true
其实就是判断Dog对象是否属于Animal类型
Animal a2 = new Animal()
a2 instanceof Animal = true;
a2 instanceof Dog = false;
a2 instanceof Animal = true;
a2 instanceof Dog = false;
子类继承的是父类的非私有成员和方法,不能继承父类的实例(也就是对象)
代码块
静态
类
静态
类
代码块
普通代码块
每一对{}都是一个代码块,大代码块可以包含中代码块,可以包含小代码块
变量的作用域就是包含变量的{}内
变量的作用域就是包含变量的{}内
构造块
直接在类中定义的代码块
class Student1{
{System.out.print("我就是构造代码块");}
}执行优先级高于构造方法
{System.out.print("我就是构造代码块");}
}执行优先级高于构造方法
静态代码块
静态
static关键字
静态属性(类变量)
利用static修饰的属性,只会开辟一个堆内存
通过一个对象访问修改时,其他对象访问获取的属性也会随着改变
因为是全局属性,可以无需通过创建对象访问
通过一个对象访问修改时,其他对象访问获取的属性也会随着改变
因为是全局属性,可以无需通过创建对象访问
类名.属性
静态方法
利用static修饰的方法,可以无需通过创建对象再通过对像调用方法
在调用时可以不创建任何对象,只能访问静态成员
在调用时可以不创建任何对象,只能访问静态成员
类名.方法
对象名.方法
对象名.方法
静态代码块
利用static修饰的代码块,类被加载时,静态代码块会执行,因类只加载一个,故静态代码块只执行一次
通常用于对类的成员变量进行初始化。
通常用于对类的成员变量进行初始化。
static修饰的成员随class文件一同加载,所以优先级最高
与访问控制权限一样,只能修饰成员变量,不能修饰局部变量(方法中的变量)
当在方法或变量前加static时,问自己:
这个方法/变量是否属于类本身,而不是某个具体对象?
它是否不依赖对象的状态就能工作?
这个方法/变量是否属于类本身,而不是某个具体对象?
它是否不依赖对象的状态就能工作?
类
抽象类和接口
抽象类
abstract修饰的类为抽象类,有(abstract 修饰)抽象方法的类必须为抽象类
抽象类可以不包含抽象方法
抽象类可以不包含抽象方法
抽象方法只需声明无需实现
一个非抽象类继承抽象,该子类必须实现抽象类的全部抽象方法
(故抽象方法不能声明为private修饰,不然子类无法实现)
(故抽象方法不能声明为private修饰,不然子类无法实现)
接口
接口继承
interface修饰接口
(克服单继承,一个类只能继承一个父类
但一个接口可以继承多个父接口)
(克服单继承,一个类只能继承一个父类
但一个接口可以继承多个父接口)
public interface 接口 extends 接口1,接口2……{
public static final 数据类型 变量名 = 常量值
public abstract 返回值类型 抽象方法名 (参数)
}
public static final 数据类型 变量名 = 常量值
public abstract 返回值类型 抽象方法名 (参数)
}
接口的实现
implements实现接口
class Dog implements Action{//重写Action接口的抽象方法}
一个类实现接口的同时继承抽象类
修饰符 class 类名 extends 父类名 implements 接口1,接口2....{}
class Dog extends Action implements Animal{}
Dog类继承了抽象类Action ,并实现了Animal接口
接口不允许继承抽象类
接口只定义方法签名(不实现),抽象类要求其继承者实现自己的抽象方法
Object类(超类)
所有类的父亲
所有类的父亲
boolean equals()
判断俩个对象是否相等
int hasfCode()
返回对象散列码值
String toString()
方法默认打印调用对象的字符串形式
重写后输出的是toString方法被重写了
重写后输出的是toString方法被重写了
class Animal2{
void shout() {
System.out.println("动物叫!");
}
public String toString(){
return "toString方法被重写了";
}
}
public class Test2 {
public static void main(String[] args) {
Animal2 a = new Animal2();
System.out.println("对象a的字符串形式为:" + a.toString());
}
}
void shout() {
System.out.println("动物叫!");
}
public String toString(){
return "toString方法被重写了";
}
}
public class Test2 {
public static void main(String[] args) {
Animal2 a = new Animal2();
System.out.println("对象a的字符串形式为:" + a.toString());
}
}
内部类
成员内部类
定义在类中的类
成员内部类可以访问外部类的成员和方法
外部类也可以访问内部类的成员和方法(包括私有)
外部类也可以访问内部类的成员和方法(包括私有)
外部类访问内部类必须通过内部类的实例
创建成员内部类对象
需要外部类实例
1.外部类名.内部类名 变量名 = new 外部类名().new 内部类名();链式调用
2.外部类名 变量名 = new 外部类名();
外部类名.内部类名 变量名 = 外部类实例 .new内部类名();
1.外部类名.内部类名 变量名 = new 外部类名().new 内部类名();链式调用
2.外部类名 变量名 = new 外部类名();
外部类名.内部类名 变量名 = 外部类实例 .new内部类名();
除非是在外部类的方法中,可以直接
内部类名 变量名 = new 内部类名();
内部类名 变量名 = new 内部类名();
局部内部类
(方法内部类)
(方法内部类)
定义在类中方法中的类
class Student{
void test2(){成员方法
class Inner{局部内部类
void show(){
System.out.print("这是局部内部类")
}
}
}
}
void test2(){成员方法
class Inner{局部内部类
void show(){
System.out.print("这是局部内部类")
}
}
}
}
局部内部类可以访问外部类的成员变量和方法(只能访问final或等效final的局部变量)
但外部类不能访问局部内部类的变量和方法(局部内部类的变量和方法只能通过所属方法访问)
但外部类不能访问局部内部类的变量和方法(局部内部类的变量和方法只能通过所属方法访问)
静态内部类
使用static修饰的成员内部类
静态内部类只能访问外部类的静态成员
但外部类可以访问静态内部类的所有成员和方法(包括私有)(通过外部类访问静态内部类成员时,可以跳过外部类直接访问内部类)
但外部类可以访问静态内部类的所有成员和方法(包括私有)(通过外部类访问静态内部类成员时,可以跳过外部类直接访问内部类)
创建静态内部类对象
不需要外部类实例
外部类名.静态内部类名 变量名 = new 外部类名.静态内部类名()
外部类名.静态内部类名 变量名 = new 外部类名.静态内部类名()
访问静态内部类的静态成员或静态方法时,可直接
外部类名.内部类名.成员或方法()
匿名内部类
没有类名的内部类
将类作为一个方法(参数是接口类型)的参数使用
(实现接口的匿名内部类)
new 父接口() {
实现部分
}
(实现接口的匿名内部类)
new 父接口() {
实现部分
}
interface Action{
void shout();
}
public class Test3 {
public static void main(String[] args) {
String name = "小花";
actionShout(new Action (){
public void shout(){
System.out.println(name + "喵喵喵……");
}
});
}
public static void actionShout(Action an){
an.shout();
}
}
void shout();
}
public class Test3 {
public static void main(String[] args) {
String name = "小花";
actionShout(new Action (){
public void shout(){
System.out.println(name + "喵喵喵……");
}
});
}
public static void actionShout(Action an){
an.shout();
}
}
参数是接口类型的方法
1.接入一个接口实现类(看上方的类与接口)
2.使用匿名内部类作为参数
异常
trycatchfinall
e.printStackTrace
自定义类的使用
class Goods{
private int id;
private String name;
private String people;
private double price;
private int number;
public Goods(){}
public Goods(int id,String name,String people,double price,int number){
this.id = id;
this.name = name;
this.people = people;
this.price = price;
this.number = number;
}
public String toString() {
return "" ;
}
//封装
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 getPeople(){return people;}
public void setPeople(String people){this.people = people;}
public Double getPrice (){return price;}
public void setPrice(double price){this.price = price;}
public int getNumber(){return number;}
public void setNumber(int number){this.number = number;}
private int id;
private String name;
private String people;
private double price;
private int number;
public Goods(){}
public Goods(int id,String name,String people,double price,int number){
this.id = id;
this.name = name;
this.people = people;
this.price = price;
this.number = number;
}
public String toString() {
return "" ;
}
//封装
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 getPeople(){return people;}
public void setPeople(String people){this.people = people;}
public Double getPrice (){return price;}
public void setPrice(double price){this.price = price;}
public int getNumber(){return number;}
public void setNumber(int number){this.number = number;}
在哪个类(主类,其他类)中使用,就要在那个类中创建该自定义类的对象
利用该对象可以调用类中方法,访问类中属性
创建对象时属于调用构造方法,构造方法属于类中,所以传入参数可以直接访问private属性
利用该对象可以调用类中方法,访问类中属性
创建对象时属于调用构造方法,构造方法属于类中,所以传入参数可以直接访问private属性
Java API
静态方法推荐类名调用方法,静态方法会返回一个对象
实例方法必须使用对象调用方法,实例方法可以通过方法链式调用调用方法(类名.静态方法.实例方法)
实例方法必须使用对象调用方法,实例方法可以通过方法链式调用调用方法(类名.静态方法.实例方法)
一、字符串类
String类
(实例方法:通过对象调用方法)
(实例方法:通过对象调用方法)
字符串的获取
字符串长度
int x = str.length()
字符串第x个字符
char x = str.charAt(x-1)
字符x第一次出现的位置
int x = str.indexOf(x)
子字符串ab
"ab"
字符x最后一次出现的位置
int x = str.lastIndexOf(x)
子字符串ab
"ab"
字符串的转换
输出内容
String x = str.toString
字符串转换为特定类型数组
char[] ca = str.toCharArray()
直接映射
直接映射
转化后字符串会被拆解成一个个字符,ca[1]=2
byte[] bt = str.grtBytes()
编码转换
编码转换
将int值转换为String类型之后的结果
String x = String.valueOf(12)
目标类型 int值
目标类型 int值
将字符串转换成大写字母之后的结果
String x = str.toUpperCase()
将字符串转换成小写字母之后的结果
String x = str.toLowerCase()
字符串的判断
(返回类型boolean)
(返回类型boolean)
判断是否以字符串Str开头
str.startsWith("Str")
判断是否以字符串ng结尾
str.endsWith("ng")
判断是否包含字符串tri
str.contains("tri")
判断字符串是否为空
str.isEmpty()
判断俩个字符串是否相等
str.equals(str1)
==用于判断俩个字符串对象的地址是否相等
equals判断俩个字符串的内容是否相等
字符串的替换和去空格
替换
String x = str.replace("it", "cn.it")
将字符串中的it替换为cn.it
去空格
去全部
String x = str.replace(" " , "")
replaceAll(常用于正则表达式,不使用正则时不推荐)
去俩端
String x = str.trim()
将字符串俩端的空格去除
字符串的截取和分割
截取
从第5个字符截取到末尾的结果
String x = str.substring(4)
从第5个字符截取到第6个字符的结果
String x = str.substring(4, 6)
包括4,不包括6
分割
将字符串转换为字符串数组
String[] strArray = str.split("-")
以-字符为切割点,将-去除
利用for循环
for (int i = 0; i< strArray.length; i++){
if( i!= str.length-1){
System.out.primt(strArray[i] + " ,"); //如果不是数组最后一个元素,在元素后面加逗号
} else {
System.out.println(strArray[i]);//数组的最后一个元素不加逗号
}
}
if( i!= str.length-1){
System.out.primt(strArray[i] + " ,"); //如果不是数组最后一个元素,在元素后面加逗号
} else {
System.out.println(strArray[i]);//数组的最后一个元素不加逗号
}
}
异常
字符串的索引都是从0开始的
角标越界异常
itcast
如果str.charAt(6)
程序会报错StringIndexOutOfBounfsException
字符串的不变性
字符串对象调用方法后本身依旧不变,要用新的字符串对象接收才能修改成功
String new =str.replace()
StringBuffer类
(实例方法:通过对象调用方法)
(实例方法:通过对象调用方法)
修改
返回对象本身(this)
便于链式调用
返回对象本身(this)
便于链式调用
添加
在末尾添加字符串
str.append("itcast")
在指定位置插入字符串
str.insert(2, "123")
将123插入原本索引为2的位置
删除
删除指定范围
str.delete(1,5)
包括1,不包括5
删除指定位置
str.deleteCharAt(2)
删除索引为2的字符
清空缓冲区
str.delete(0, str.length())
删除一切
替换
字符串反转
str.reverse()
替换指定位置字符串或字符
str.replace(1,3,"qq")
包括1,不包括3
特例:无返回值(离散操作)
替换指定位置
str.setCharAt(1, 'p')
将索引为1的字符替换为p
一定要单引号
不能写进System.out.print()里面,先调用,再输出str
转换
输出内容
String x = str.toString
查询
长度
int x = str.length()
指定位置字符
char x = str.charAt(x)
StringBuilder类
与Buffer相似
方法未整理
setLength重置sb,避免一直重新实例化
差异
量
String类表示的字符串是常量,创建后内容和长度无法改变
进行replace()操作后,调用方法的String本对象身并不会改变
要用新对象接收String new = str.replace()
要用新对象接收String new = str.replace()
StringBuffer和StringBulider表示字符容器,内容和长度可以随时修改
如果字符串仅用于表示数据类型,使用String即可
使用reverse(),调用方法的StringBuffer或StringBulider对象本身改变
安全
如果需要对字符串内容进行修改的,使用StringBuffer和StringBuilder
要求线程安全使用StringBuffer,不要求线程安全的使用StringBuilder
String类的replace,substring,split是返回一个新的String类对象,
原本String对象内容不变,String不变o
原本String对象内容不变,String不变o
而StringBuffer和StrimgBulider类修改得是对象本身
效率
StringBuilder>StringBuffer>String
相等
StrinBuffer和StringBuilder没有被Object中的equals()方法覆盖
操作符+
String类可以利用+连接对象,StringBuffer类的对象之间不能
二、System类
Runtime类
Runtime类
System类(提供静态(所有)的属性和方法)
(静态方法:通过类名调用方法)
(静态方法:通过类名调用方法)
arraycopy()
System.arraycopy(Object src, int srcPos, Object dext, int dextPos, int length)
源数组,源数组开始位置,目标数组,目标数组开始位置,复制长度
目标数组必须要有足够空间,不然会出现角标越界异常
int[] formArray = {0,1,2,3,4,5};
int[] toArray = {6,7,8,9,10,11,12};
System.arraycopy(formArray, 2, toArray,3,4);
toArray{6,7,8,2,3,4,5}
int[] toArray = {6,7,8,9,10,11,12};
System.arraycopy(formArray, 2, toArray,3,4);
toArray{6,7,8,2,3,4,5}
currentTimeMillis()
Long time = System.currentTimeMillis()
获取当前系统时间,返回long类型的值,该值表示
从1970年1月1日0点0分0秒到现在之间的时间差,单位是毫秒,该值也称为时间戳。
从1970年1月1日0点0分0秒到现在之间的时间差,单位是毫秒,该值也称为时间戳。
很好用,用来计算程序的运行时间,end-start
getProperties() 和 getProperty()
System.getProPerty()
根据系统属性名获取对应属性值
String value = System.getProperty(key);
获取当前key(键)对应的value(值)
System.getProperties()
详细见集合-Map-HashTable-Properties集合
详细见集合-Map-HashTable-Properties集合
获取当前系统的全部属性
返回一个Properties对象,其中封装了系统的所有属性
属性是以键值对形式存在的(一个集合),用Properties类型接收
属性是以键值对形式存在的(一个集合),用Properties类型接收
变量类型必须与方法返回类型兼容
Properties pro = System.getProperties();
void gc()
System.gc()
使Java虚拟机立即进行垃圾回收
当一个对象在内存中被释放时,它的finalize()方法会被自动调用,因此可以在类中定义finalize 方法来观察
void exit()
System.exit(int status)
使当前正在运行的Java虚拟机终止运行
status :0表示正常退出(不会再运行后面代码),非0表示异常退出
Runtime类( 用于封装Java虚拟机进程)
典型的单例模式,(实例方法:利用对象调用方法)
典型的单例模式,(实例方法:利用对象调用方法)
每次使用Java启动虚拟机都对应一个Runtime实例,并且只能有一个。
因此在定义Runtime类的时候,它的构造方法已经私有化了,同时对象不可以直接实例化。
若想在程序中获得一个Runtime实例,只能:
Runtime run = Runtime.getRuntime();
因此在定义Runtime类的时候,它的构造方法已经私有化了,同时对象不可以直接实例化。
若想在程序中获得一个Runtime实例,只能:
Runtime run = Runtime.getRuntime();
获取虚拟机信息
处理器个数
int x = run.availableProcessors()
空闲内存数量
long x = run.freeMemory()
默认单位B(字节)。在括号后()/1024/1024后为M
最大可用内存数量
long x = run.maxMemory()/1024/1024 +"M"
虚拟机中内存总量
long x = run.totalMemory()/1024/1024 + "M"
操作系统进程
Process x = run.exec(" notepad.exe")
代码调用此方法,将参数传给exec(),运行程序时会在桌面打开一个记事本文件
exec()方法会返回一个Process对象,此对象为exec()生成的新进程
获取对象:Process pro = run.exec("notepad.exe")
Thread.sleep(3000)
程序休眠3秒(3000毫秒 )
pro.destroy()
杀掉进程
三、Math类
Random类
Random类
Math类(提供大量静态方法)
多重载
返回类型与参数类型一致
返回类型与参数类型一致
计算-10的绝对值
Math.abs(-10)
10
求2.1和-2.1中的较大值
Math.max(2.1,-2.1)
2.1
求2.1和-2.1中的较小值
Math.min(2.1,-2.1)
-2.1
参数double,返回double
计算4的开平方结果
Math.sqrt(4)
2.0
默认是double类,4会隐式转化为double,返回double类
求大于5.6的最小整数
Math.ceil(5.6)
6.0
求小于-4.2的最大整数
Math.floor(-4.2)
-5.0
计算指数函数2的3次方的值
Math.pow(2,3)
8.0
参数float,返回int
double long
double long
对-4.6进行四舍五入
Math.round(-4.6)
-5
取到最接近的整数
无参数,返回double
生成一个大于等于0.0小于1.0的随机值
Math.random()
0.6357164628
random可以扩展不仅限于0-1的随机数
Random类(电脑语句)
(实例方法:通过对象调用方法)
(实例方法:通过对象调用方法)
无参构造方法
import java.util.Random(把java下的Random引入)
Random dn = new Random();
int x =dn.nextInt( 随机数个数,3,则为0,1,2其中一个)
Random dn = new Random();
int x =dn.nextInt( 随机数个数,3,则为0,1,2其中一个)
有参构造方法
Random dn = new Random(13);
int x =dn.nextInt( 随机数个数,3,则为0,1,2其中一个)
int x =dn.nextInt( 随机数个数,3,则为0,1,2其中一个)
无参构造时,系统会以当前时间戳作为种子来产生随机数
有参(种子)时,当种子相同时,则产生的随机数有相同的序列
常用方法
生成 float类型随机数
dn.nextFloat()
生成double类型随机数
dn.nextDouble()
生成int 类型随机数
dn.nextInt()
生成0~100的随机数
dn.nextInt(100)
包括0,不包括100
四、日期时间类(java.time包)
Instant类(表示时刻,代表时间戳)
(Epoch 代表 计算机元年) 从1970.1.1.00:00:00到当前的毫秒值
(Epoch 代表 计算机元年) 从1970.1.1.00:00:00到当前的毫秒值
从系统获取当前时刻
Instant now = Instant.now()
计算机元年增加毫秒数
Instant ins = Instant.ofEpochMilli(1000*60*60*24)ofEpochMilli()是静态方法,可以直接通过类名调用
结果:1970-01-02T00:00:00Z
计算机元年增加秒数
Instant ins = Instant.ofEpochSecond(60*60*24)
结果:1970-01-02T00:00:00Z
获取的秒数
long x = Instant.parse("2007-12-03T10:15:30.44Z).getEpochSecond()
方法链式调用Instant.parse()静态方法创建并返回实例,后接.getEpochSecond()实例方法
获取的纳秒数
int x = Instant.parse("2007-12-03T10:15:30.44Z").getNano()
Instant ins = Instant.parse("2007-12-03T10:15:30.44Z");
System.out.print(ins.getNano());
System.out.print(ins.getNano());
将时间对象转换为Instant实例
Instant x = Instant.from()
参数必须为OffsetDateTime或ZonedDateTime这种含时区的对象
OffsetDateTime是固定偏移量
ZonedDateTime zdt = ZonedDateTime.now(ZoneId.of("Asia/guangdong"))含时区ID规则
LocalDate类(日期,不包含时间)
获取日期对象的俩个方法
now()
LocalDate now1 = LocalDate.now()
of(int year, int month, int dayOfMonth)
LocalDate date = LocalDate.of(2025, 4, 19)
常用方法
LocalDate的获取与格式化
从LocalDate实例获取的年份
int x = now.getyear()
of
of
从LocalDate实例获取的月份
int x = now.getMonthValue()
of
of
从LocalDate实例获取的日期(当天是本月第几天)
int x = now.getDayOfMonth()
of
of
格式化
String x = now.format(DateTimeFormatter.ofPattern("yyyy年MM月dd日")
LocalDate的判断
(返回类型是boolean)
(返回类型是boolean)
判断日期of是否在now之前
of.isBefore(now)
now of
now of
判断日期of是否在now之后
of.isAfter(now)
now of
now of
判断日期of和now是否相等
of.equals(now)
now of
now of
判断日期of是否是闰年
of.isLeapYear()
now
now
LocalDate解析和加减
(返回的都是新对象)
(返回的都是新对象)
将日期字符串解析为日期对象
String st = "2025-4-15"
LocalDate x = LocalDate.parse(st)
LocalDate x = LocalDate.parse(st)
将LocalDate实例增加x
LocalDate x = now.plusYears(x)
of Months(x)
Days(x)
of Months(x)
Days(x)
时间单位为复数
将LocalDate实例减x
LocalDate x = now.minusDays(x)
of Years(x)
Months(x)
of Years(x)
Months(x)
将LocalDate实例指定x
LocalDate x = now.withYear(x)
of Month(x)
Day(x)
of Month(x)
Day(x)
时间单位为单数
LocalTime类(时间,不包含日期)
获取时间对象的俩个方法
now()
LocalTime now = LocalTime.now()
of(int hour, int minute, int second)
LocalTime of = LocalTome.of(9, 23, 23)
常用方法
LocalTime的获取与格式化
从LocalTime实例获取的小时
int x = now.getHour()
从LocalTime实例获取的分钟
int x =now.getMinute()
从LocalTime实例获取的秒数
int x = now.getSecond()
从LocalTime实例获取的无毫秒时间
LocalTime x = now.withNano(0)
格式化
String x = now.format(DateTimeFormatter.ofPattern("HH:mm:ss"))
直接sout→now→2025-4-24
LocalTime的判断
(返回类型是boolean)
(返回类型是boolean)
判断时间of是否在now之前
of.isBefore(now)
之后,相等:类LocalDate
LocalTime的解析和加减
(返回的都是新对象)
(返回的都是新对象)
将时间字符串解析为时间对象
LocalTime x = LocalTime.parse("12:15:30")
将LocalTime实例增加x
LocalTime x = now.plusHours(x)
of Minutes(x)
Seconds(x)
of Minutes(x)
Seconds(x)
时间单位为复数
将LocalTime实例减少x
LocalTime x = now.minusHours(x)
of Minutes(x)
Seconds(x)
of Minutes(x)
Seconds(x)
将LocalTime实例指定x
LocalTime x = now.withHour(x)
of Minute(x)
0Second(x)
of Minute(x)
0Second(x)
时间单位为单数
LocalDate Time类(日期和时间)
默认格式2025-4-21T23:51:47.774Z
实例化
LocalDateTime now = LocalDateTime.now()
LocalDateTime of = LocalDateTime.of(2005,07,24,5,7,24,223)
获取时间
打印now
转换为LocalDate类
LocalDate date = now.toLocalDate
转换
转换为LocalTime类
LocalTime time = now.toLocalTime
指定格式(格式化)
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy年MM日dd日hh时mm分ss秒SSS毫秒")
打印now.format(dtf)
Duration类(基于时间值测量时间量)
Period类(计算日期差,只能精确到年月日)
Clock类(时钟系统,用于查找当前时刻)
五、包装类
将基本数据类型的值包装为引用数据类型的对象
基本数据类型
byte
char
int
short
long
float
double
boolean
byte
char
int
short
long
float
double
boolean
对应包装类
Byte
Character
Integer
Short
Long
Float
Double
Boolean
Byte
Character
Integer
Short
Long
Float
Double
Boolean
装箱(将基本类型包装)
int a = 20;
Integer in = a;(自动装箱)
Integer in = a;(自动装箱)
(自动)将int值直接赋值给对应包装类
拆箱(将引用类型拆为基本类型)
int b = in;(自动拆箱)
(自动)将包装类对象赋值给基本类
Integer类具有object类所有方法
Integer特有方法
手动拆箱
Integer in = new Integer(20):(手动装箱)
手动就是创建新的对象
int a = in.intValue() + 10;(手动拆箱同时加减)
将指定int值转换为Integer实例
Integer in = Integer.valueOf(10);
与手动装箱等价,更推荐此方法
不仅仅可是int类,字符串"45"也可以通过这方法转换
将字符串类型的数值转换为int类型
int b = Integer.parseInt("20")+32
52(基本数据int类型)
差异
除Character外,包装类都有valueOf(String s)方法,将字符串转换为相应类型的包装类,但(字符串不能是null,必须是相应包装类的基本类型的数据),否则会报错
Byte a = Byte.valueOf("55")
Short b = Short.valueOf("55")
Double c = Double.valueOf("55.3")
填写45的int虽然会隐式转换,但还是推荐直接填写double类型的数据
除Character外,包装类都有parseXXXX(String s)静态方法,将字符串转换为对应基本类型,但(字符串不能是null,必须是可以解析为相应基本类型的数据),否则报错
int a = Integer.parseInt("123")
double b = Double.parseDouble("123")
特性
与集合框架兼容,集合可以存储包装类,不可以存储基本类型
泛型支持,<T>必须是对象类型,不能是基本类型
包装类允许null值,基本类型不允许,自动拆箱时null值会导致异常抛出
不可变性,Integer i = 10;实例一旦创建,其值不可改变
i = i+5;实际上是创建了一个新的对象
i = i+5;实际上是创建了一个新的对象
六、正则表达式
元字符
\转义字符
^正则表达式的开头标志
$正则表达式的结尾标志
*匹配零次或多次
+匹配一次或多次
?匹配一次或零次
.匹配任意字符
{n}匹配n次
{n,}至少匹配n次
{n,m}最少匹配n次,最多匹配m次,n<m
x|y| 匹配x或y
[xyz] 字符集合,匹配包含x或y或z的字符,返回单个字符及其位置
[a-z] 匹配指定范围的任意字符
[^a-z] 匹配不在该范围的任意字符
[a-zA-Z] 匹配a-z到A-Z
[0-9] 匹配任意数字
\d 匹配数字0-9
\D 匹配非数字字符
\D 匹配非数字字符
\s 匹配空白字符
\S 匹配非空白字符
\S 匹配非空白字符
\w 匹配单词字符和0-9
\b 单词边界 匹配位置(位置一侧是单词字符,另一侧是非单词字符),不是实际字符
(\\bcat\\b,匹配"The cat in"中的"cat"但不匹配category中的"cat")
\B 非单词边界 匹配位置(位置两侧字符类型相同)
(\\Bcat\\B,匹配"category"中的"cat"但不匹配独立"cat")
(\\bcat\\b,匹配"The cat in"中的"cat"但不匹配category中的"cat")
\B 非单词边界 匹配位置(位置两侧字符类型相同)
(\\Bcat\\B,匹配"category"中的"cat"但不匹配独立"cat")
\A 输入的开头(匹配整个输入字符串的开头(不受多行模式影响)
(\\AStart,只匹配"Start here\nStart again"中的第一个"Start")
\z 输入的结尾(严格,绝对结尾)
(end\\z, "This is the end\n"匹配失败(因为\z要求绝对结尾))
\Z 输入的结尾(宽松,允许后面有换行符,匹配换行符前)
(end\\Z, "This is the end\n"匹配成功(因为\Z允许在换行符前匹配))
(\\AStart,只匹配"Start here\nStart again"中的第一个"Start")
\z 输入的结尾(严格,绝对结尾)
(end\\z, "This is the end\n"匹配失败(因为\z要求绝对结尾))
\Z 输入的结尾(宽松,允许后面有换行符,匹配换行符前)
(end\\Z, "This is the end\n"匹配成功(因为\Z允许在换行符前匹配))
\G 上一个匹配的结尾
单词字符通常包括字母、数字和下划线(即 `[a-zA-Z0-9_]`)
非单词字符则是除此之外的字符,如空格、标点符号等。
非单词字符则是除此之外的字符,如空格、标点符号等。
Pattern类
Matcher类
Matcher类
Pattern类
用于创建一个正则表达式,构造方法是私有的,不能直接创建对象
可以通过简单工厂方法创建一个
Pattern p = Pattern.compile("\+元字符")
可以通过简单工厂方法创建一个
Pattern p = Pattern.compile("\+元字符")
常用方法
匹配Pattern的输入模式(静态方法)
Pattern.matches(String regex, CharSequence input)
正则表达式 输入字符串
正则表达式 输入字符串
Pattern.matches("\\d+" , "2223")→true
Pattern.matches("\\d+" , "2223aa")→false(布尔类型)
Pattern.matches("\\d+" , "2223aa")→false(布尔类型)
方法链式调用
// 以下两行代码完全等效:
boolean result1 = Pattern.matches("\\d+", "2223");
boolean result2 = Pattern.compile("\\d+").matcher("2223").matches();
切割方法(实例方法)
split()
将指定字符串按正则表达式匹配的部分切分成多个子字符串
String[] str = p.split("我的QQ是:456456我的电话是:0532214我的邮箱是:aaa@aaa.com");
会依据\d+将数字部分切除,并以切除点为间隔做数组str[1]="我的QQ号是:"
Matcher类
作用功能
Matcher`对象的主要功能是:
1. 在目标字符串中查找匹配的子串。
2. 检查是否完全匹配。
3. 获取匹配的详细信息(如匹配的位置、分组内容等)。
1.复用 Pattern:Pattern,编译正则表达式后,可以重复用于不同字符串。
2.灵活的匹配操作:Matcher 支持多种匹配方式(完全匹配、部分匹配、迭代匹配等)。
3.性能优化:预编译 Pattern`后,匹配操作更高效。
匹配器(Matcher)对指定字符串进行正则表达式匹配操作
方法
(前提Pattern p = Pattern.compile("\\d+"))
(前提Pattern p = Pattern.compile("\\d+"))
完全匹配
返回创建Matcher对象的Pattern对象(实际返回的是正则表达式)
m.pattern()→\d+
但System.out.println(m.pattern() == p); // 输出true,证明是同一个对象
Matcher m = p.matcher("22bb23")→m.matches()→false
创建对象 ("2223")→m.matches()→true
创建对象 ("2223")→m.matches()→true
使用的是对象p的正则表达式
matches不受find方法影响
部分匹配
对前面字符串匹配
m.lookingAt()
lookingAt会推动匹配器的搜索指针
如果先用lookingAt,那么find的第一次会返回23,第二次find会返回false
如果先用find,那么find会在第三次才返回false,而之后接lookingAt还是返回22
如果先用find,那么find会在第三次才返回false,而之后接lookingAt还是返回22
lookingAt()每次调用都会重置指针位置,在匹配成功后将指针停留在匹配字符后
所以会有find,find,lookingAt,find,lookingAt,lookingAt,find,find
22,23,22,23,22,22,23,false
22,23,22,23,22,22,23,false
任何位置进行匹配
m.find()
22bb23
每一次find成功后搜索指针都会移动到对应值后一位字符,下一次将从相应位置开始搜索
m.find()→true
m.group()→22
m.group()→22
m.find()→true
m.group()→23
m.group()→23
m.find()→false
位置
匹配字符的起始索引
m.start()
匹配字符的结束索引+1
m.end()
he l lo ,12 3world
0123456789
0123456789
使用此方法前应该先进行匹配操作(find,lookingAt,matches)
输出匹配到的字符
m.ground()——123
输出匹配到的字符
m.ground()——123
m.start()——6
m.end()——9
String类的正则表达式
替换
.replaceAll()
String str = "A1B222C3D45E5".replace("\\d+" , "_")
A_B_C_D_E
完全匹配
.matches()
String str = "123456789abc".("\\d+")
false
切割
.spliit()
String str = "SDFH7DGS8UFH9".split("\\d+")
for(int i = 0; i<str.length; i++){
System.out.print(str[i] + " ");
}
System.out.print(str[i] + " ");
}
SDFH DGS UFH
七、I/O类
键入语句
import java.util.Scanner(把java.uitl下的Scanner引入)
Scanner (操作者)= new Scanner(System.in);
System.out.println("请输入XX");
数据类型 变量 =(操作者).next();
String name =(操作者).next();
int a =(操作者).nextInt();
System.out.println(变量)
Scanner (操作者)= new Scanner(System.in);
System.out.println("请输入XX");
数据类型 变量 =(操作者).next();
String name =(操作者).next();
int a =(操作者).nextInt();
System.out.println(变量)
判断对错
利用为内存单元赋值比较大小
执行到user.next()时程序会发生阻塞,这很关键
while(){
StringBuffer sb = new StringBuffer;
sb.addend(content);
content=user.nextLine();
}
StringBuffer sb = new StringBuffer;
sb.addend(content);
content=user.nextLine();
}
用户输入作为最后一步会在用户回车后直接执行循环条件判断
如果反过来,每次用户输入回车后会直接将内容添加到sb中
例如循环条件是!content.equals("stop"),输入内容不是stop时循环
那么这个顺序可以确保,在回车后执行判断后退出
如果反过来,回车后会先读取添加后再执行循环条件判断
那么这个顺序可以确保,在回车后执行判断后退出
如果反过来,回车后会先读取添加后再执行循环条件判断
标准输入输出流是 System.in 和 System.out(及 System.err),它们是 Java 运行时环境预定义的流实例。
而 InputStream/OutputStream 是抽象基类,用于描述所有字节流的通用行为。
而 InputStream/OutputStream 是抽象基类,用于描述所有字节流的通用行为。
处理换行符
nextXxx
不会消费行尾换行符,导致下次读取是空字符串
使用后立即使用nextLine()清空换行符
nextLine
会消费换行符
最推荐用nextLine() + 类型转换
Java 集合
集合基础
# 接口的实现类详解(index是索引)
## 接口与实现类的基本概念
**接口(Interface)** 是一种抽象类型,它定义了一组方法签名(行为规范),但不提供具体实现。接口只规定"做什么",不规定"怎么做"。
**实现类(Implementation Class)** 是具体实现了接口中所有抽象方法的类。它提供了接口定义的方法的具体实现细节。
## 主要实现类的含义
"接口的主要实现类"通常指:
1. 某个接口最常用、最标准的实现类
2. JDK或框架中提供的默认实现类
3. 在特定上下文中被推荐使用的实现类
## 示例说明
以Java中的`List`接口为例:
```java
// 接口定义
public interface List<E> {
boolean add(E e);
E get(int index);
int size();
// 其他方法...
}
// 主要实现类
public class ArrayList<E> implements List<E> {
// 具体实现接口中的所有方法
public boolean add(E e) {
// 实现细节...
}
public E get(int index) {
// 实现细节...
}
public int size() {
// 实现细节...
}
}
// 另一个实现类
public class LinkedList<E> implements List<E> {
// 不同的实现方式
public boolean add(E e) {
// 实现细节...
}
// 其他方法实现...
}
```
在这个例子中:
- `List`是接口
- `ArrayList`和`LinkedList`都是`List`的实现类
- `ArrayList`通常被认为是`List`接口的主要实现类,因为它在大多数情况下是默认选择
## 实现类的特点
1. **必须实现所有抽象方法**:除非是抽象类,否则必须实现接口中的所有方法
2. **可以有自己的额外方法**:实现类可以包含接口中没有定义的方法
3. **遵循接口契约**:实现必须符合接口定义的行为规范
4. **多实现**:一个类可以实现多个接口
## 为什么使用接口和实现类
1. **解耦**:将定义与实现分离
2. **多态**:可以通过接口类型引用不同的实现类对象
3. **可替换性**:可以轻松更换不同的实现而不影响客户端代码
4. **标准化**:定义统一的编程契约
理解接口和实现类的关系是面向对象编程中的重要概念,它体现了"针对接口编程,而不是针对实现编程"的原则。
简略图
Collection
子接口
List(有序)
主要实现类
ArrayList
LinkedList(双向链表)
Vector(线程安全)
Set(无序)
主要实现类
HashSet
LinkedHashSet(双向链表)
TreeSet(元素排序)
Map
主要实现类
HashMap(无序)
LinkedHashMap(双向链表)
TreeMap(元素排序)
HashTable(线程安全)
Properties
集合(java.util包)
遍历集合元素
迭代器Iterator接口
Iterator it = list.iterator()
获取接口对象
获取接口对象
判断集合对象list是否有下一个对象
while(it.hasNext()){
Object obj = it.next();//如果有对象(true),将其用于创建集合Object的对象obj
System.out.print(obj);//j将集合打印出来
}
while(it.hasNext()){
Object obj = it.next();//如果有对象(true),将其用于创建集合Object的对象obj
System.out.print(obj);//j将集合打印出来
}
如果想在遍历时删除对象,可以使用if
if("张三".equals(obj)) {
it.remove();
}
it.remove();
}
foreach循环
循环遍历
for(Object obj : 对象名){
System.out.print(obj);取出并打印
}
for(Object obj : 对象名){
System.out.print(obj);取出并打印
}
只能访问,不能修改
Collection
(单列集合根接口)
(存储符合某规则的元素)
(单列集合根接口)
(存储符合某规则的元素)
子接口
List接口
(元素有序可重复,有索引
存入顺序和取出顺序一致)
(元素有序可重复,有索引
存入顺序和取出顺序一致)
实现类
主要实现类
ArrayList集合
(长度可变的数组)
(长度可变的数组)
创建集合
ArrayList al = new ArrayLiat();
每次增加或删除元素时,ArrayList会创建新的数组,
这就是ArrayLisy存入顺序和取出顺序一致的具体表现,
也是ArrayLiat集合效率低的原因,但因允许通过索引访
问元素,所以ArrayLiat集合查询元素很快
这就是ArrayLisy存入顺序和取出顺序一致的具体表现,
也是ArrayLiat集合效率低的原因,但因允许通过索引访
问元素,所以ArrayLiat集合查询元素很快
特有方法
(无返回值)
(无返回值)
void = al.trimToSize()
将ArrayList的容量调整为列表大小,最小化存储空间
void = al.ensureCapacity(int minCapacity)
增加ArrayList的容量,避免频繁扩容
LinkedList集合
(长度可变的数组)
(长度可变的数组)
创建集合
LinkedList ll = new LinkedList();
双向循环链表,—A—B— ,增加或删除元素时,改变的是元素的前后关系
丨— C — |
这就是LinkedList存入顺序和取出顺序一致的原因,也是与ArrayList集合
相比增加删除元素效率高的原因
丨— C — |
这就是LinkedList存入顺序和取出顺序一致的原因,也是与ArrayList集合
相比增加删除元素效率高的原因
特有方法
(返回指定类型的元素)
LinkedList<String>,返回String
LinkedList<Integer>,返回Integer
(返回指定类型的元素)
LinkedList<String>,返回String
LinkedList<Integer>,返回Integer
void = ll.addFirst(o)
addLast(o)
addLast(o)
将指定元素o添加到开头或结尾
E x = ll.getFirst()
getLast()
getLast()
返回并返回第一个或最后一个元素
E x = ll.removeFirst()
removeLast()
removeLast()
删除并返回第一个或最后一个方法
Vector集合
(线程安全)
(线程安全)
方法
修改
存入元素
boolean x = list.add(o)
void = list.add(index,o)
boolean x = list.add(index,collection)
void = list.add(index,o)
boolean x = list.add(index,collection)
返回boolean,默认存到数组尾,但如果在元素前添加(索引, "一"),则会添加至指定位置。
也可以(1,al1)将集合al1插入al索引为1的位置
也可以(1,al1)将集合al1插入al索引为1的位置
删除元素
boolean x = list.remove(o)
E x = remove(index)
E x = remove(index)
只能删除单个元素,返回boolean, 支持index 和 obj 删除
清空集合
void = list.clear()
无返回值,不能打印,可通过调用此方法后,打印调用 size看看是否为0
替换并返回旧元素
E x = list.set(index, newo)
将索引1的元素李四替换为张三,并返回原本的元素李四
获取
获取元素
E x = list.get(index)
不修改,只获取
数组长度
int x = list.size()
获取元素的索引
int x = list.indexOf(o)
lastIndexOf(o)
lastIndexOf(o)
基本类型10,20
字符类'1'
字符串类"123"
字符类'1'
字符串类"123"
获取子集合
List<E> x = list.subList(startindex,endindex)
切割集合包含start,不包含end,返回一个新的集合(为list的子集合)
判断
包含指定元素
boolean x = list.contains(o)
不允许索引
集合是否为空
boolean x = list.isEmpty()
Set接口
(元素无序不可重复,无索引
通过哈希表对应对象)
(元素无序不可重复,无索引
通过哈希表对应对象)
实现类
主要实现类
HashSet集合
(通过哈希表对应元素)
(通过哈希表对应元素)
创建集合
HashSet hs = new HashSet();
常用方法:无特有方法,其方法继承于Collection和Set 接口
集合唯一性的核心机制
在调用add()方法时,HashSet集合会默认调用hashCode()和equals()方法判断对象
存入自定义类的对象时
需要重写Object类的hashCode()和equals()方法
才能确保不会重复录入(以id确认是否重复)
才能确保不会重复录入(以id确认是否重复)
import java.util.*;
class Student {
private String id;
private String name;
public Student(String i , String n){
name = n;
id = i;//以id来确认是否重复
}
//重写方法
@Override
public String toString(){
return id+ ":" + name;//改变集合的存储方式[ 1: 张三 ,2:李四]
}
public int hashCode(){
return id.hashCode();//返回id的哈希值
}
public boolean equals(Object o){//o是Student类的实例
if (this==o){
return true;//对象o存在于Student类,终止
}
if (!(o instanceof Student)){//
return false;//对象o不属于Student类,终止(强制转换会异常)
}
Student s = (Student) o;
boolean b = id.equals(s.id);
return b;
}
}
class Student {
private String id;
private String name;
public Student(String i , String n){
name = n;
id = i;//以id来确认是否重复
}
//重写方法
@Override
public String toString(){
return id+ ":" + name;//改变集合的存储方式[ 1: 张三 ,2:李四]
}
public int hashCode(){
return id.hashCode();//返回id的哈希值
}
public boolean equals(Object o){//o是Student类的实例
if (this==o){
return true;//对象o存在于Student类,终止
}
if (!(o instanceof Student)){//
return false;//对象o不属于Student类,终止(强制转换会异常)
}
Student s = (Student) o;
boolean b = id.equals(s.id);
return b;
}
}
执行 equals 方法
|
├── 检查是否是同一个对象 (this == o)
│ ├── 是 → 返回 true
│ └── 否 → 进入下一步
│
├── 检查是否是 Student 类型 (o instanceof Student)
│ ├── 否 → 返回 false
│ └── 是 → 进入下一步
│
├── 强制转换为 Student 类型 (Student s = (Student) o)
│
└── 比较 id 字段 (id.equals(s.id))
├── 相等 → 返回 true
└── 不相等 → 返回 false add添加成功
|
├── 检查是否是同一个对象 (this == o)
│ ├── 是 → 返回 true
│ └── 否 → 进入下一步
│
├── 检查是否是 Student 类型 (o instanceof Student)
│ ├── 否 → 返回 false
│ └── 是 → 进入下一步
│
├── 强制转换为 Student 类型 (Student s = (Student) o)
│
└── 比较 id 字段 (id.equals(s.id))
├── 相等 → 返回 true
└── 不相等 → 返回 false add添加成功
LinkedHashSet集合
创建集合
LinkedHashSet lhs = new LinkedHashSet();
存入顺序和取出顺序一致,但元素不可重复的类
与LinkedList类一致,使用双向链表结构
与LinkedList类一致,使用双向链表结构
TreeSet集合
可对元素进行排序
通过比较方法判定重复
可对元素进行排序
通过比较方法判定重复
存入自定义类对象时
Comparable接口(基本类型的包装类,String类抖实现了该接口)
自然排序
自定义类实现Comparable接口,并重写compareTo()方法
自定义类实现Comparable接口,并重写compareTo()方法
class Student implements Comparable<Student>{
类属性(id,name)
类方法
构造方法
public String toString(){
return id + ":" + name;
}
//指定泛型类型了
public int compareTo(Student o){
return 0;//集合只要一个元素
return 1;//集合按怎么存就怎么取
return -1;//集合按存入元素的倒序进行存储
}
}
类属性(id,name)
类方法
构造方法
public String toString(){
return id + ":" + name;
}
//指定泛型类型了
public int compareTo(Student o){
return 0;//集合只要一个元素
return 1;//集合按怎么存就怎么取
return -1;//集合按存入元素的倒序进行存储
}
}
比较器排序
自定义类实现Comparator接口,并重写compare()方法
自定义类实现Comparator接口,并重写compare()方法
匿名内部类实现接口学习
TreeSet ts = new TreeSet(new Comparator(){
public int compare(object o1 , object o2){//对比o1和o2的大小
return -1;//存入的反序存储
}
});
TreeSet ts = new TreeSet(new Comparator(){
public int compare(object o1 , object o2){//对比o1和o2的大小
return -1;//存入的反序存储
}
});
如果跟上面一样显式实现接口,
参数便是俩个srudent对象
参数便是俩个srudent对象
方法
List 基于索引的操作方法Set是没有的
修改
存入元素
boolean x = set.add(o)
与List的差别在于,List允许元素重复从而总是返回true
而Set不允许元素重复,如果元素已有则返回false
并且Set不允许通过索引添加元素
而Set不允许元素重复,如果元素已有则返回false
并且Set不允许通过索引添加元素
删除元素
boolean x = set.remove(o)
只能删除单个元素,返回boolean
清空集合
void = set.clear()
获取
数组长度
int x = set.size()
判断
包含指定元素
boolean x = set.contains(o)
集合是否为空
boolean x = set.isEmpty()
Map
(双列集合根接口)
(存储键值映射关系的元素,
一个键一个值,键不可重复
值可重复,多个键可对应一个值)
映射=绑定
(双列集合根接口)
(存储键值映射关系的元素,
一个键一个值,键不可重复
值可重复,多个键可对应一个值)
映射=绑定
实现类
主要实现类
HashMap集合
(键值对无序)
(键值对无序)
创建集合
HashMap hm = new HashMap();
键值对无序,通过哈希表定位键值对实现高效存取
允许null值(一个多个),非线程安全
允许null值(一个多个),非线程安全
LinkedHashMap集合
创建集合
LinkedHashMap lhm = new LinkHashMap();
存入顺序和取出顺序一致,但键值映射的类
与LinkedList类一致,使用双向链表结构
与LinkedList类一致,使用双向链表结构
TreeMap集合
(可对键值排序)
(可对键值排序)
HashTable集合
(线程安全)
(线程安全)
Properties集合
常与JAVA API中的System类使用
创建集合
Properties p = new Properties();
方法
p.propertyNames()
将对象改为System类名时,为静态方法
可以获取系统属性的key
将对象改为System类名时,为静态方法
可以获取系统属性的key
返回一个包含所有key的Enumeration对象
利用对象接收(使用对象p)
Enumeration en = p.propertyNames();
迭代打印(使用对象en,p)
while(en.hasMoreElements()){
String key =(String) en.nextElement();
String value = p.getProperty(key);
System.outprintln(key + ":" +value)
}
将p改为System 类名时,可以打印系统配置的键值
Enumeration en = p.propertyNames();
迭代打印(使用对象en,p)
while(en.hasMoreElements()){
String key =(String) en.nextElement();
String value = p.getProperty(key);
System.outprintln(key + ":" +value)
}
将p改为System 类名时,可以打印系统配置的键值
方法
修改
绑定键值
V x = ma.put(key, value)
如果绑定的键已绑定,则新value覆盖旧value,与key形成新的映射关系
第一个参数必定是键,第二个参数必定是值
第一个参数必定是键,第二个参数必定是值
键存在:返回旧值,并用新值覆盖旧值
键不存在:添加并返回null
键不存在:添加并返回null
删除键对应的值,并返回被删值
V x = ma.remove(key)
键存在:返回被删值
键不存在:返回null
键不存在:返回null
清除所有键值绑定
void = ma.clear()
获取
获取键绑定的值
(若无,返回null)
(若无,返回null)
V x = ma.get(key)
返回的是对应类型的值V,可通过int a = ma.get(key),自动拆箱
ma.put(key, a+1)来实现投票功能
ma.put(key, a+1)来实现投票功能
键存在:返回值
键不存在:返回null
键不存在:返回null
绑定键值的个数
int x = ma.size()
判断
键是否绑定值7
boolean x = ma.containsKey(key)
值是否绑定一个或多个键
(值可重复)
(值可重复)
boolean x = ma.containsValue(value)
视图*
Set<K类型如String> keys = ma.keySet()
访问所有键(唯一,Set类型)
Collection<V类型如Integer> values = ma.values()
访问所有值(允许重复,Collection类型)
Set(<Map.Entry<String,Integer>>可省 ) sm = ma.entrySet()
访问所有键值对(最灵活,Set<Entry> 类型)
使用的案例可在Java 2025的Test项目中查看Vote.java
以不同的视角操作 Map集合,而无需手动处理键值对的底层细节
视图:与Map动态关联,操作双向生效,只提供访问方式本身不存储数据
视图:与Map动态关联,操作双向生效,只提供访问方式本身不存储数据
泛型
本质是占位符,“<T>”就是泛型
泛型类和泛型对象
泛型类
public class 类名<T1, T2, T3> { // 使用任意合法的标识符作为类型参数
private T1 name; // 使用泛型参数 T1
private T1 name; // 使用泛型参数 T1
protected T2 age; // 使用泛型参数 T2
private T3 money; // 使用泛型参数 T3
public T1 show(T1 n) {
System.out.print(n);
return n;
}
}
}
public class 类名<String,Integer,Double,……>{
private String name;
protecr Integer age;
private Double money;
public String show (String n){
System.out.print(n);
}
}(错误写法)
private String name;
protecr Integer age;
private Double money;
public String show (String n){
System.out.print(n);
}
}(错误写法)
泛型对象
类名<> 对象名 = new 类名<>()
泛型方法
泛型方法所在类可以是泛型类也可以不是
定义
public <T> void show(T t){
System.out.print(t);
}
System.out.print(t);
}
当创建对象调用时,对象名 .show(参数)
传入的参数是什么类型,返回值就是什么类型
传入的参数是什么类型,返回值就是什么类型
泛型接口
接口初知:interface在接口中的作用等价于类的class
implements的作用等价于extends(继承)
implements的作用等价于extends(继承)
定义
public interface Inter<T>{
public abstract void show(T t);
}
public abstract void show(T t);
}
定义接口子类
1.直接在接口中指定具体类型
子类为接口的T明确具体类型
public InterImpl implements Inter<String>{
public void show (String s){
System.out.print(s);
}
}
public InterImpl implements Inter<String>{
public void show (String s){
System.out.print(s);
}
}
main方法{//实现类测试
Inter<String> in = new InterImpl<>();//子类的实例化
in.show("hello");
}
Inter<String> in = new InterImpl<>();//子类的实例化
in.show("hello");
}
2.在子类的定义上声明泛型类型
子类的T传递接口的T
public InterImpl<T> implements Inter<T>{
public void show (T t){//T由子类实例化时决定
System.out.print(t);
}
}
public InterImpl<T> implements Inter<T>{
public void show (T t){//T由子类实例化时决定
System.out.print(t);
}
}
main方法{//实现类测试
Inter<String> in = new InterImpl<>();//子类的实例化
in.show("hello");
Inter<Integer> in1 = new InterImpl<>();//子类的实例化
in.show(12);
}
子类实例化时,泛型<>的类型传递给接口父类
Inter<String> in = new InterImpl<>();//子类的实例化
in.show("hello");
Inter<Integer> in1 = new InterImpl<>();//子类的实例化
in.show(12);
}
子类实例化时,泛型<>的类型传递给接口父类
类型通配符
无界通配符
public void processList(List<?> list) {
// ✅ 读取元素(视为Object)
for (Object obj : list) {
System.out.println(obj);
}
// ❌ 写入元素(编译错误)
list.add("Hello"); // 错误!无法确定具体类型
list.add(null); // ✅ 唯一允许的写入操作(null无类型)
}
Java I/O(输入/输出)
java.io包
File类
创建File对象
File f = new File()
仅仅创建一个路径的抽象表示
常用方法
修改
实际创建文件
boolean x = f.creatNewFile()
实际创建目录
boolean x = f.getParentFile().mkdir()
D:\\生产工具\\javatest\\javatext1\\test.txt
if( ! f.getParentFile().exists()){//获取对象的对应目录(文件) 的父目录,若不存在
f.getParentFile().mkdir()}.创建单层指定的javatest1目录,且其父目录javatest目录必须存在
if( ! f.getParentFile().exists()){//获取对象的对应目录(文件) 的父目录,若不存在
f.getParentFile().mkdir()}.创建单层指定的javatest1目录,且其父目录javatest目录必须存在
boolean x = f.getParentFile().mkdirs()
递归创建多层目录(自动创建所有缺失父目录)
删除对象创建的文件或空文件夹
boolean x = f.delete()
如果f对象是文件夹,且文件夹有子内容,则此方法不生效
此方法只允许删除一个空的文件夹
此方法只允许删除一个空的文件夹
获取
获取对象的表示的文件(夹)的名称
对应路径(相/绝)
绝对路径
对应目录(文件)的父目录
对应路径(相/绝)
绝对路径
对应目录(文件)的父目录
String x = f.getName()
f.getPath()
f.getAbsolutePath()
f.getParentFile()
f.getPath()
f.getAbsolutePath()
f.getParentFile()
计算机元年到文件最后修改时间的毫秒值
long x = f.lastModified()
获取文件内容长度
long x = f.length()
判断
返回boolean
返回boolean
判断对象对应的文件或目录是否存在
f.exists()
判断对象对应的文件或目录是否可读
可写
可写
f.canRead()
f.canWrite()
f.canWrite()
判断对象对应的是否为文件
为目录
文件或目录是否是绝对路径
为目录
文件或目录是否是绝对路径
f.isFile()(判断路径存在的同时判断是否是文件,比exists高级)
f.isDirectory()
f.isAbsolute()
f.isDirectory()
f.isAbsolute()
数组
String[] x = f. list()
返回String[]列出指定目录全部内容(名称)
File[] x = f. listFiles()
返回一个包含File对象所有子文件和子目录的数组
返回的的数值不包含File对象本身JavaTest(D:\\Java\\JavaTest)
返回的的数值不包含File对象本身JavaTest(D:\\Java\\JavaTest)
实例
输出或删除对象的所有文件
foreach循环
File f = new File("D:\\javaTest"); 这段代码算
gainevery(f);//获取一切,方法调用 一次遍历
} //传入对象f
public static void gainevery(File f){
File[] fs = f.listFiles();//利用File数组实例fs接收 对象f的所有子文件和子目录
for (File receive : fs){//for循环,创建File对象receive(接收),遍历fs??
if (receive.isDirectory()){//如果对象f存在子文件或子目录,则为true??此错误
// 如果对象f是目录则为true
// 如果没有这个,则无法获取文件夹1下的文件
// (如果对象f是目录,则调用方法重新遍历,直到没有目录时)
gainevery(receive);////如果对象f存在子目录,则调用此方法本身再次遍历??
}
System.out.println(receive.getAbsolutePath());}
递归调用不会打断当前循环,而是进入新的方法调用处理子目录。
处理完子目录后,会回到当前循环继续执行后续代码
gainevery(f);//获取一切,方法调用 一次遍历
} //传入对象f
public static void gainevery(File f){
File[] fs = f.listFiles();//利用File数组实例fs接收 对象f的所有子文件和子目录
for (File receive : fs){//for循环,创建File对象receive(接收),遍历fs??
if (receive.isDirectory()){//如果对象f存在子文件或子目录,则为true??此错误
// 如果对象f是目录则为true
// 如果没有这个,则无法获取文件夹1下的文件
// (如果对象f是目录,则调用方法重新遍历,直到没有目录时)
gainevery(receive);////如果对象f存在子目录,则调用此方法本身再次遍历??
}
System.out.println(receive.getAbsolutePath());}
递归调用不会打断当前循环,而是进入新的方法调用处理子目录。
处理完子目录后,会回到当前循环继续执行后续代码
/*第一次时receive.isDirectory()判断的压缩包rar,为false直接输出(这属于第一次递归)
第二次时receive.isDirectory()判断的文件夹1,为true,递归调用,此时传入的receive是文件夹1内的文件夹a
第一次递归receive.isDirectory()判断的文件夹1内的文件夹a,为true,递归调用,此时传入的receive是文件夹a内的文档txt
第二次递归receive.isDirectory()判断的文件夹a内的文档txt,为false,递归结束回到for 循环,打印路径
*/
第二次时receive.isDirectory()判断的文件夹1,为true,递归调用,此时传入的receive是文件夹1内的文件夹a
第一次递归receive.isDirectory()判断的文件夹1内的文件夹a,为true,递归调用,此时传入的receive是文件夹a内的文档txt
第二次递归receive.isDirectory()判断的文件夹a内的文档txt,为false,递归结束回到for 循环,打印路径
*/
{
File fa = new File("D:\\生产工具\\Java\\Java IO\\ByteText\\JavaTest");//删除JavaTest文件
fadelete(fa);
}
}
public static void fadelete(File fa){
if (fa.exists()){//如果JavaTest文件夹存在
File[] fason = fa.listFiles();//将JavaTest的所有子文件或子文件夹赋值给数组!!!!!不包含JavaTest!!!
for (File delete : fason){//将fason循环遍历
if (delete.isDirectory()){
fadelete(fa);//如果遍历的是文件夹,递归调用,直到最内层的文件,
}else {
delete.delete();//然后false调用delete.delete()删除文件
}
}
fa.delete();//如法炮制,直到 fa对象的JavaTest没有子内容!!此方法每次递归调用fadelete(fa)s时都会调用,但因为有子内容,FIle的delete方法不允许直接删除
} //所以直到fa对象JavaTest文件夹为空,此方法才生效;
}
File fa = new File("D:\\生产工具\\Java\\Java IO\\ByteText\\JavaTest");//删除JavaTest文件
fadelete(fa);
}
}
public static void fadelete(File fa){
if (fa.exists()){//如果JavaTest文件夹存在
File[] fason = fa.listFiles();//将JavaTest的所有子文件或子文件夹赋值给数组!!!!!不包含JavaTest!!!
for (File delete : fason){//将fason循环遍历
if (delete.isDirectory()){
fadelete(fa);//如果遍历的是文件夹,递归调用,直到最内层的文件,
}else {
delete.delete();//然后false调用delete.delete()删除文件
}
}
fa.delete();//如法炮制,直到 fa对象的JavaTest没有子内容!!此方法每次递归调用fadelete(fa)s时都会调用,但因为有子内容,FIle的delete方法不允许直接删除
} //所以直到fa对象JavaTest文件夹为空,此方法才生效;
}
子主题
字节流
(throws Exception)
(throws Exception)
顶级(抽象)父类
抽象代表无法直接
实例化这俩个顶级父类
抽象代表无法直接
实例化这俩个顶级父类
InputStream
(字节输入流)
外部 到程序
(字节输入流)
外部 到程序
常用方法
-1用于标识流的末尾
int x = is.read();返回从输入流读取并转为的整数
read(byte[] b);将从输入流读取的字节保存到字节数组,返回读取的字节数
read(byte[] b, int off ,int len);将从输入流读取的字节保存到字节数组,off指起始索引,len表示长度
read(byte[] b);将从输入流读取的字节保存到字节数组,返回读取的字节数
read(byte[] b, int off ,int len);将从输入流读取的字节保存到字节数组,off指起始索引,len表示长度
读文件(文件必须存在)
FileInputStream
FileInputStream
public class StreamTest { //必要添加抛出异常
public static void main(String[] args) throws Exception {
InputStream fis = null;
try{
fis = new FileInputStream("test.txt");//位于根目录,即与src在同一文件夹
int a = 0;
while(true){
a = fis.read();
if (a==-1){
break;
}
System.out.println(a);
}
}finally {
if (fis != null) {
fis.close();
}
}
}
}
public static void main(String[] args) throws Exception {
InputStream fis = null;
try{
fis = new FileInputStream("test.txt");//位于根目录,即与src在同一文件夹
int a = 0;
while(true){
a = fis.read();
if (a==-1){
break;
}
System.out.println(a);
}
}finally {
if (fis != null) {
fis.close();
}
}
}
}
void is.close();关闭此输入流,并关闭与此有关的所有系统资源
OutputStream
(字节输出流)
程序到外部
(字节输出流)
程序到外部
常用方法
void = os.write(int b);向输入流写入一个字节
write(byte[] b);将字节数组的所有字节写入输入流
write(byte[] b , int off , int len) 将字节数组的所有字节写入输入流,off指起始索引,len指长度
write(byte[] b);将字节数组的所有字节写入输入流
write(byte[] b , int off , int len) 将字节数组的所有字节写入输入流,off指起始索引,len指长度
写文件(文件不存在会创建,存在会清空再写入
若参数为true,则是添加而不是清空写入
FileOutputStream
若参数为true,则是添加而不是清空写入
FileOutputStream
public static void main(String[] args) throws IOException{
//OutputStream os = new OutputStream()?抽象类不能实例化
//父类引用指向子类?
OutputStream os = new FileOutputStream("test1.txt");//位于根目录,即与src在同一文件夹
String s ="欢迎您!";
byte[] b = s.getBytes();//String类方法,之所以是getBytes()是因为要编码转换
for(int i = 0 ; i<b.length; i++){
os.write(b[i]);
}
//有true时,是添加。无true时,是覆盖
OutputStream os1 = new FileOutputStream("test1.txt",true);//位于根目录,即与src在同一文件夹
String s1 ="来到清远!";
byte[] b1 = s1.getBytes();
for(int i = 0 ; i<b1.length; i++){
os.write(b1[i]);
}
os1.close();
}
}
//父类引用指向子类?
OutputStream os = new FileOutputStream("test1.txt");//位于根目录,即与src在同一文件夹
String s ="欢迎您!";
byte[] b = s.getBytes();//String类方法,之所以是getBytes()是因为要编码转换
for(int i = 0 ; i<b.length; i++){
os.write(b[i]);
}
//有true时,是添加。无true时,是覆盖
OutputStream os1 = new FileOutputStream("test1.txt",true);//位于根目录,即与src在同一文件夹
String s1 ="来到清远!";
byte[] b1 = s1.getBytes();
for(int i = 0 ; i<b1.length; i++){
os.write(b1[i]);
}
os1.close();
}
}
void = os.flush();刷新此输出流并强制写出所有缓冲的输出字节
void = os.close();关闭此输出流,并关闭与此有关的所有系统资源
文件复制
public static void main(String[] args) throws Exception {
InputStream is = new FileInputStream("test/你好.txt");//先在根目录创j建
OutputStream os = new FileOutputStream("test1/你好.txt");//无需创建,保证目录存在即可
int cd ;
long copystarttime = System.currentTimeMillis();
while((cd=is.read())!=-1){
os.write(cd); 核心步骤,如果字节不为-1将其写入os对象对应文件
}
long copyendtime = System.currentTimeMillis();
System.out.println(copyendtime-copystarttime + "毫秒");
is.close();
os.close();
}
}
InputStream is = new FileInputStream("test/你好.txt");//先在根目录创j建
OutputStream os = new FileOutputStream("test1/你好.txt");//无需创建,保证目录存在即可
int cd ;
long copystarttime = System.currentTimeMillis();
while((cd=is.read())!=-1){
os.write(cd); 核心步骤,如果字节不为-1将其写入os对象对应文件
}
long copyendtime = System.currentTimeMillis();
System.out.println(copyendtime-copystarttime + "毫秒");
is.close();
os.close();
}
}
字节缓冲流
(包装流)
(包装流)
BufferedInputStream
BufferedOutputStresm
字符流
(throws Exception)
(throws Exception)
顶级(抽象)父类
抽象代表无法直接
实例化这俩个顶级父类
抽象代表无法直接
实例化这俩个顶级父类
Reader
(字符输入流)
(字符输入流)
直接读取字符(而非字节)(文件必须存在)
FileReader
FileReader
//读取一个文档的内容
Reader r = new FileReader("ReaderTest.txt");
int ch;//因为char<int,char是16位无符号整数,int是32位有符号整数包含-1来标识流的末尾,也包含0-65535的char范围
while ((ch = r.read()) != -1) {
System.out.print((char)ch);
}
Reader r = new FileReader("ReaderTest.txt");
int ch;//因为char<int,char是16位无符号整数,int是32位有符号整数包含-1来标识流的末尾,也包含0-65535的char范围
while ((ch = r.read()) != -1) {
System.out.print((char)ch);
}
常用方法
int x = read() // 读取一个字符,返回0-65535的整数(Unicode字符),如果到达文件末尾则返回-1
read(char[] cbuf) // 读取多个字符到字符数组cbuf,返回实际读取的字符数
read(char[] cbuf, int off, int len) // 读取最多len个字符到数组cbuf的off位置开始处
read(char[] cbuf) // 读取多个字符到字符数组cbuf,返回实际读取的字符数
read(char[] cbuf, int off, int len) // 读取最多len个字符到数组cbuf的off位置开始处
Writer
(字符输出流)
(字符输出流)
直接写入字符(与FileOutputStream类似)
FileWriter
FileWriter
//创建文档并写入新内容 不存在会自动创建,但不会创建缺失父目录
Writer w = new FileWriter("WriterTest.txt");
String str = "我是WriterTest.txt文件" ;
w.write(str); str-->w
//往WriterTest,txt文档添加新内容
Writer w = new FileWriter("WriterTest.txt",true);
Writer wnew = new FileWriter("WriterTest.txt",true);
String strnew = "我被添加到WriterTest.txt文档中";
w.write(strnew);
wnew.write(strnew);
代码末尾一定要调用close方法,这样才能保存刷新文件,在文件内才能看见内容
FileWriter在写入数据时使用了缓冲区。如果不调用close()或flush(),缓冲区的内容可能不会写入磁盘。
close()方法会自动触发flush(),确保数据持久化。
Writer w = new FileWriter("WriterTest.txt");
String str = "我是WriterTest.txt文件" ;
w.write(str); str-->w
//往WriterTest,txt文档添加新内容
Writer w = new FileWriter("WriterTest.txt",true);
String strnew = "我被添加到WriterTest.txt文档中";
w.write(strnew);
FileWriter在写入数据时使用了缓冲区。如果不调用close()或flush(),缓冲区的内容可能不会写入磁盘。
close()方法会自动触发flush(),确保数据持久化。
多个对象可以对应同一个文件
常用方法
void = write(int c) // 写入一个字符(低16位)
write(char[] cbuf) // 写入整个字符数组
write(char[] cbuf, int off, int len) // 写入字符数组的一部分
write(String str) // 写入一个字符串
write(String str, int off, int len) // 写入字符串的一部分
write(char[] cbuf) // 写入整个字符数组
write(char[] cbuf, int off, int len) // 写入字符数组的一部分
write(String str) // 写入一个字符串
write(String str, int off, int len) // 写入字符串的一部分
文件复制
大同小异,先在src创建一个文档copy.txt
后在IDEA内创建一个Reader r = new FileReader("copy.txt") //读取文档
再创建一个Writer w = new FileWriter("copyend.txt")//对象来自动创建一个 copyend文档
String int str;//获取读取的文本
while((str = r.readLine() ) != -1){//如果不是流末尾,readLine是BufferedReader类的方法
w.write(str);//将str获取的文本写入w对象文件
}
后在IDEA内创建一个Reader r = new FileReader("copy.txt") //读取文档
再创建一个Writer w = new FileWriter("copyend.txt")//对象来自动创建一个 copyend文档
while((str = r.read
w.write(str);//将str获取的文本写入w对象文件
}
字符缓冲流
(包装流)速度比普通Reader和Writer快,字符越多越明显
(包装流)速度比普通Reader和Writer快,字符越多越明显
BufferedReader
String line = br.readLine();
BufferedReader类对象调用读取一行文本
BufferedReader的参数是一个FileReader对象,不能是字符串指定文件
BufferedReader类对象调用读取一行文本
BufferedReader的参数是一个FileReader对象,不能是字符串指定文件
常用方法
readLine()
读取空行返回空字符串""
读取到流末尾时(无数据时),才返回null
BufferedWriter
void = bw.newLine()
调用这个方法是不是添加新的一行,而是结束这一行
bw.writer("1"); 1 1
bw.newLine(); 结束1的一行
bw.newLine(); 结束空白行 2
bw.writer("1"); 1 1
bw.newLine(); 结束1的一行
bw.newLine(); 结束空白行 2
转换流
将字节流转换为字符流
InputStreamReader
(Reader子类)
(Reader子类)
InputStreamReader isr = new InputStreamReader(new FileInputStream("test.txt"));
OutputStreamWriter
(Writer子类)
(Writer子类)
OutputStreamReader osr = new OutputStreamWriter(new FileOutpiutStream("test.txt"));
用于指定编码
Reader r = new FileReader(path);
Reader r = 【new InputStreamReader(new FileInputStream(path), "UTF-8");】匿名对象调用
Reader r = 【new InputStreamReader(new FileInputStream(path), "UTF-8");】匿名对象调用
父类指向子类
Writer w = new FileWriter(path);
Writer w = 【new OutputStreamWriter(new FileOutputStream(path), StandardCharsets.UTF_8);】匿名对象调用
Writer w = 【new OutputStreamWriter(new FileOutputStream(path), StandardCharsets.UTF_8);】匿名对象调用
IO输入流·
建议统一使用循环读取文件,循环是通解
byte[] buf = new byte[1024];
int len;
while ((len=is.read(buf))!=-1) {//批量读取,减少性能消耗
System.out.println(new String(buf,0,len));
}
int len;
while ((len=is.read(buf))!=-1) {//批量读取,减少性能消耗
System.out.println(new String(buf,0,len));
}
如果很小,不建议使用循环,在TCP通信中会出错
客户端发送后,服务器端会进入读取,直到客户关闭流(os.close),否则一直阻塞
但客户如果在os.write后,进行os.close(),那么客户和服务器端的连接会断开
但客户如果在os.write后,进行os.close(),那么客户和服务器端的连接会断开
所以如果很小,直接去掉循环(只读取一次)
len = is.read(buf);
System.out.println(new String(buf,0,len));
len = is.read(buf);
System.out.println(new String(buf,0,len));
建议在每次os.write后直接os.flush()刷新输出流
缓冲区
作用
缓冲区是一块临时的内存区域,位于 JVM 堆内存中(不是在物理设备上)
批量读写,减少物理访问次数
对于输出流需要flush或close才能写入外部
类型
无缓冲区
FileInputStream,FileOutputStream,Reader,Writer
自带缓冲区(8kb)
BufferedInputStream,BufferedOutputStresm,BufferedReader,BufferedWriter
微缓冲区(用于编码转换2kb)
InputStreamReader,OutputStreamWriter
创建缓冲区
无缓冲区的流可以手动创建,但建议直接使用带缓冲区的流
byte[] buf = new byte[1024]
byte[] buf = new byte[1024]
有缓冲区的流不要额外创建,可能导致错乱
Java 多线程
Thread类
实现多线程
继承java.lang包的Thread类
(子类对象开启线程)
(子类对象开启线程)
继承并重写Thread的run()方法,并使用Thread类的start()方法启动新线程
//不能将文件名(=主类名)设为Thread(因为start方法出错)
MyThread mt = new MyThread();实例化对象
可以是Thread mt = new MyThread();(父类引用指向子类)
mt.start();开启新线程——————————————→
while(true){
System.out.print("main方法正在运行")
}
MyThread mt = new MyThread();实例化对象
可以是Thread mt = new MyThread();(父类引用指向子类)
mt.start();开启新线程——————————————→
while(true){
System.out.print("main方法正在运行")
}
//Java只支持单继承
class MyThread extends Thread{//继承
public void run(){//重写
while(true){
System.out.print("MyThread的run方法运行中")
}
}
}
class MyThread extends Thread{//继承
public void run(){//重写
while(true){
System.out.print("MyThread的run方法运行中")
}
}
}
实现java.lang.Runnable接口
(接口只有一个方法run)
(Thread类对象开启线程)
(接口只有一个方法run)
(Thread类对象开启线程)
为Thread类提供一个实现了Runnable接口的对象作为参数
必须为Mythread,因为实现接口并没有继承Thread类,不能进行父类引用指向子类
class MyThread implements Runnable{
public void run (){
while(true){
System.out.print("MyThread实现了Runnable接口")
}
}
}
public void run (){
while(true){
System.out.print("MyThread实现了Runnable接口")
}
}
}
MyThread mt = new MyThread();
Thread t = new Thread (mt);实现接口的对象为参数
t.start();开启新线程
while(true){
System.out.print("main方法正在运行")
}
Thread t = new Thread (mt);实现接口的对象为参数
t.start();开启新线程
while(true){
System.out.print("main方法正在运行")
}
Thread t = new Thread(mt,"线程1")
Lambda表达式简化Runnable实现的语法
public class JDK8NewCreate {
public static void main(String[] args) {
Thread t = new Thread(()->{//匿名内部类的新写法,是实现接口开启新线程,而不是继承重写开启
while (true){
System.out.println("new Thread");
}
});
t.start();
while(true){
System.out.println("main方法运行中");
}
}
}
public static void main(String[] args) {
Thread t = new Thread(()->{//匿名内部类的新写法,是实现接口开启新线程,而不是继承重写开启
while (true){
System.out.println("new Thread");
}
});
t.start();
while(true){
System.out.println("main方法运行中");
}
}
}
实现方式的对比
实现Runnable接口的实现方式可以避免JAVA单继承的限制
方法
(均无返回值)
(均无返回值)
run方法
多线程的实现所必须的
不创建新线程,如果方法内是死循环,那这方法下面的方法永远调用不了
休眠(静态)
Thread.sleep(),单位是毫秒
与Java API RunTime的操作系统进程挂钩可去回看
获取当前线程(静态,返回Thread对象)
Thread t = Thread.currentThread()
一般获取的是main线程
如果是在实现了Runnable接口的类中调用,获取的是该类对象的线程
一般获取的是main线程
如果是在实现了Runnable接口的类中调用,获取的是该类对象的线程
开启新线程(实例)
t.start()
开启新线程时,系统会自动调用run方法
继承并开启
new MyThread().start()
实现接口并开启
new.Thread(mt).start()
必须传入实现了Runnabel接口的对象mt
必须传入实现了Runnabel接口的对象mt
设置线程名(实例)
t.setName("线程1")
创建时设名
new Thread(mt, "线程1").start()
Ⅱ
Ⅱ
mt对象的类必须实现Runnable接口
MyThread mt = new MyThread();
Thread t = new Thread(mt);
t.setName("线程1");
t.start();
Thread t = new Thread(mt);
t.setName("线程1");
t.start();
获取线程后改名
Thread.currentThread().setName("线程1")
获取线程名(实例)
t.getName();
通过对象获取
Thread t = Thread.currentThread()
String str = t.getName()
String str = t.getName()
获取线程后获取
String s = Thread.currentThread().getName()
其余方法在线程控制学习
线程的生命周期及状态转换
新建状态(New)
创建对象并获得内存
就绪状态(Runnable)
调用strat()方法从新建状态进入就绪状态
调用notify()将阻塞状态(wait()进入的)的线程唤醒进入就绪状态
sleep/join/Io完成从阻塞状态进入就绪状态
运行状态(Running)
获得CPU使用权进入,执行run()方法
只有就绪状态才能进入运行状态
阻塞状态(Blocked)
运行中被打断进入
如sleep(),另一个线程的join()
如sleep(),另一个线程的join()
引起阻塞原因消除后进入就绪状态
利用锁对象调用wait(),lock.wait()
死亡状态(Terminated)
调用stop(),run()执行完毕,抛出异常时进入
一旦进入停止运行,无法再转换状态
推荐使用标记if,while来终止,stop已废弃
线程调度
(方法均无返回值)
(方法均无返回值)
优先级
t.setPriority(int p)
(Thread静态常量)
(Thread静态常量)
参数1~10,值越大,优先级越高
Thread.MAX_PRIORITY 等价于10
Thread.MIN_PROORITY 等价于1
Thread.NORM_PRIORITY 等价于5
Thread.MIN_PROORITY 等价于1
Thread.NORM_PRIORITY 等价于5
休眠
(throws Exception)
(throws Exception)
Thread.sleep(int s)
s单位为毫秒,使当前线程休眠s毫秒
让步
Thread.yield()
将当前线程转为就绪状态(sleep是让程序进入阻塞状态)
与if()搭配,if时执行Thread.yield()
只是一个建议,具体看操作系统
与if()搭配,if时执行Thread.yield()
只是一个建议,具体看操作系统
插队
t.join()
使当前线程进入阻塞状态,直到t对象的线程执行完
等待
t.wait()
利用锁对象!
进入就绪状态
t.notify()
中断线程
t.interrupt()
是一个请求,不会强制中断
主线程
多线程中主线判断各个线程修改的结果的操作
主线程中:
while(true){
主线程中:
while(true){
if(各个线程修改的结果){
break;
}
}
多线程同步
多个线程同时处理共享资源会出现线程安全问题
想要线程安全
必须保证任何时刻只能有一个线程访问共享资源
想要线程安全
必须保证任何时刻只能有一个线程访问共享资源
同步代码块
synchronized关键字创建同步代码块
相关配合方法
lock.notify()
notifyAll()
notifyAll()
随机唤醒一个等待该对象的线程,加All会唤醒所有等待的线程
lock.wait()
wait(long x)
wait(long x)
释放锁
如果没有设定时间,会进入无限等待时期,直到被notify唤醒
synchronized(lock){
}
}
lock是关键,是一个锁对象,多个线程共享的锁对象必须是唯一的
Object lock = new Object();lock可以是任意类型对象。
Object lock = new Object();lock可以是任意类型对象。
当线程1执行同步代码块时,线程2无法执行同步代码块会发生阻塞,直到线程1执行完
同步方法
使用synchronized关键字修饰的方法
某一时刻只允许一个线程访问
private synchronized void saleTicket(){
}
}
同步方法的锁是当前调用该方法的对象(this指向的对象)
静态同步方法的锁是该方法所在类的class对象(通过类名.class获取对象)
(静态方法可以用sunchornized关键字修饰)
(静态方法可以用sunchornized关键字修饰)
死锁问题
俩个线程各自占用着对方执行完毕所需的锁,但是都无法释放自己的锁
Lambbda表达式
// 传统匿名内部类
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("线程运行");
}
}).start();
// Lambda 简化写法
new Thread(() -> System.out.println("线程运行")).start(); // ✅ 一行搞定!
()是接口方法的参数对象
Java 网络编程
网络通信协议
InetAddress类
构造方法
获得本地主机
InetAddress la = InetAddress.getLocalHost()
输出获得:主机名/IP地址
静态方法调用,获取的是当前运行代码的计算机的IP地址
确认特定主机的IP地址
InetAddress test = InetAddresss.getByName("www.baidu.com")
则www.baidu.com的IP地址被固定住
常用方法
获取IP地址字符串
String x = la.getHostAddress()
子主题
获得主机名
String x = la.getHostName
本机则为计算机名
不是则为主机名,www.baidu.com
否则为IP地址
不是则为主机名,www.baidu.com
否则为IP地址
指定时间内地址能否到达
boolean x = la.isReachable()
UDP通信
(封装)DatagramPacket类
构造方法
接收端对象
(无IP地址和端口号)
(无IP地址和端口号)
DatagramPacket dpjs = new DatagramPacket(byte[] buf , int length)封装数据的字节数组,数据的大小(通常是buf.length)
(bytr[] buf , int offset , int length)将接收到的数据从buf数据的offset处开始放入
(bytr[] buf , int offset , int length)将接收到的数据从buf数据的offset处开始放入
发送端对象
(有IP地址和端口号
IP地址和端口号是接收端Scocket对象的)
(有IP地址和端口号
IP地址和端口号是接收端Scocket对象的)
DatagramPacket dpfs = new DatagramPacket(byte[] buf , int length , InetAddress addr , int port)封装数据的字节数组,数据的大小,接收端IP地址,接收端端口号
(byte[] buf , int offset , int length , InetAddress addr , int port)
(byte[] buf , int offset , int length , InetAddress addr , int port)
常用方法
获取IP地址
InetAddress x = dp.getAddress()
dpjs返回dsfs的IP地址
dpfs返回dsjs的IP地址
dpfs返回dsjs的IP地址
一般还包含主机名,返回InetAddress对象
dp.getAddress().getHostAddress()就只有单IP地址
一个InetAddress实例
dp.getAddress().getHostAddress()就只有单IP地址
一个InetAddress实例
获取端口号
int x = dp.getPort()
dpjs返回dsfs的端口号
dpfs返回dsjs的端口号
dpfs返回dsjs的端口号
返回操作的数据
byte[] b = dp.getData()
返回参数中的byte[] buf
返回操作数据的长度
int x = dp.length()
返回参数中的int length
更新发送包
(一般用在发送端)
(一般用在发送端)
void = dpfs.setData(buf)
包对象在实例化时就会封装使用的buf,更新buf并不能更新包,应该调用方法
复原接收包
(一般用在接收端)
(一般用在接收端)
dpjs.setLength()
在接收处理数据(输出等)后,用于复原对象(而不是频繁使用setData)
(收发)DatagramSocket类
构造方法
接收端对象
(IP,端口号是本地的)
(IP,端口号是本地的)
DatagramSocket dsjs = new DatagramSocker(int port)
(int port , InetAddress addr)
(int port , InetAddress addr)
发送端对象
(IP,端口号是本地的)
(IP,端口号是本地的)
DatagramSocket dsfs = new DatagramSocker()系统分配一个未被使用的端口号
(int port) 指定端口号
(int port , InetAddress addr)指定端口号,IP地址
(int port) 指定端口号
(int port , InetAddress addr)指定端口号,IP地址
常用方法
接收数据并填充
(dp接收时:作为缓冲区容器,被动填充)
(dp接收时:作为缓冲区容器,被动填充)
void = ds.receive(dp)
接收数据并封装到DatagramPacket对象中
发送数据包
(dp发送时:主动构造完整数据包)
(dp发送时:主动构造完整数据包)
void = ds.send(dp)
发生DatdgramPacket对象(数据包)
关闭释放资源
void = ds.close()
关闭当前Socket对象ds,释放为其保留的资源
端口号的对应
dpjs获取的是dsfs的端口号
dpjs对应dsfs
dpfs获取的dsjs的端口号
dpfs对应dsjs
数据的发送
DatagramPacket(dpfs)——> DatagramSocket(dsfs)——>DatagramSocket(dsjs)——>DatagramPacket(dpjs)
dpfs不需要IP和端口,它只是将dpfs发送出去
dpjs不需要IP和端口,它只是将dsjs接收的数据封装
dpfs要指定dsjsIP和端口
dsjs要设置IP和端口
使用
创建缓冲区byte[] buf = new buf[1024],长度buf.length
在发送端创建dpfs和dsfa
在接收端创建dpjs和dsjs
发送端的dpfs要指定接收端dsjs的IP地址和端口号
dsjs要设置IP地址和端口号
发送端的dpfs在创建时会封装buf,如果此时buf为空,那么在用户输入后转化buf = message.getByte()后
要调用dpfs.setData(buf)更新,然后dsfs.send(dpfs)
要调用dpfs.setData(buf)更新,然后dsfs.send(dpfs)
接收端dsjs.receive(dpjs),将接收的数据封装进dpjs,
在转换时如果直接new String(buf),此时会因为缓冲区未被填满导致字符串具有大量空符
应该new String(dpjs.getData(),0,dpjs.getLength());
在处理完(如输出)包后,调用dpjs.setLength()复原
在转换时如果直接new String(buf),此时会因为缓冲区未被填满导致字符串具有大量空符
应该new String(dpjs.getData(),0,dpjs.getLength());
在处理完(如输出)包后,调用dpjs.setLength()复原
TCP通信
服务器端
ServerSocket类
构造方法
ServerSocket ss = new ServerSocket()不推荐,无法直接使用,需搭配bind()方法绑定端口
(int port)端口号(0时随机分配)
(int port , int backlog)端口号,保持连接请求等待数量
(int port , int backlog , InetAddress bindAddr)端口号,等待数量,规定对象在哪个IP地址或网卡等待连接
(int port)端口号(0时随机分配)
(int port , int backlog)端口号,保持连接请求等待数量
(int port , int backlog , InetAddress bindAddr)端口号,等待数量,规定对象在哪个IP地址或网卡等待连接
常用方法
操作
等待客户连接(阻塞状态)
ss.accept()
返回一个Socket类的对象表示客户端
Socket client = ss.accept()
Socket client = ss.accept()
将无参构造的ServerSocket对象绑定到指定IP和端口
void = ss.bind(ipport)
参数为一个封装了IP和端口的SocketAddress对象
SocketAddress ipport = new InetSocketAddress("192.168.1.100", 8080);
或
InetAddress ip = InetAddress.getByName("localhost");
SocketAddress ipport = new InetSocketAddress(ip, 8080);
SocketAddress ipport = new InetSocketAddress("192.168.1.100", 8080);
或
InetAddress ip = InetAddress.getByName("localhost");
SocketAddress ipport = new InetSocketAddress(ip, 8080);
获取
获取服务器端IP地址
InetAddress x = ss.getInetAddress()
返回一个封装了ss绑定的IP地址的InetAddress对象
InetAddress ssip = ss.getInetAddress()
InetAddress ssip = ss.getInetAddress()
判断
判断服务器端状态
boolean x = ss.isClosed()
判断ss是否处于关闭状态
客户端
Socket类
构造方法
Socket s = new Socket()还需调用connect(ipport)方法来提供服务器IP和服务器端口
(String host , int port)服务器IP地址(直接指定) ,服务器端口号
(InetAddress address , int port)服务器IP地址(由InetAddress对象提供),服务器端口号
(String host , int port)服务器IP地址(直接指定) ,服务器端口号
(InetAddress address , int port)服务器IP地址(由InetAddress对象提供),服务器端口号
常用方法
获取
获取端口号
int x = s.getPort()
返回远程端的端口号(服务器端调用→客户端端口号)
(客户端调用→服务器端口号)
(客户端调用→服务器端口号)
int x = s.getLocalPort()
返回本地端的端口号
获取客户端的IP地址
InetAddress x = s.getInetAddress()
返回一个封装了客户端绑定的IP地址的InetAddress对象
InetAddress sip = s.getLocalAddress();
InetAddress sip = s.getLocalAddress();
InetAddress x = s.getLocalAddress()
服务器端IP
操作
读取数据
s.getInputStream()
返回一个InputStream()对象
服务器端的对象调用,读取客户端发送的数据
InputStream,从外部向程序的
所以可以这么理解
程序(服务器端) ← 外部(客户端)
所以可以这么理解
程序(服务器端) ← 外部(客户端)
客户端的对象调用,读取服务器端发送的数据
发送数据
s.getOutputStream()
返回一个OutputStream()对象
服务器端的对象调用,向客户端发送数据
客户端的对象调用,向服务器端发送数据
关闭连接
s.close()
应先将与其相关的所有输入/输出流关闭,在关闭Socekt连接(释放所有资源)
四元组唯一性
TCP通信的关键
四元组
客户端IP:端口 + 服务端IP:端口
四元组唯一即可建立通信
客户端的参数需指定服务器的IP地址和服务器的端口号
使用
服务器端创建ServerSocket对象后调用accept并用Socket接收
客户端创建Socket对象
然后分别使用Socket对象调用
读取数据
s.getInputStream()
返回一个InputStream()对象
发送数据
s.getOutputStream()
返回一个OutputStream()对象
发送数据
os.write(text.getByte())
os.flush
os.flush
接收数据
IO输入流·
建议统一使用循环读取文件,循环是通解
byte[] buf = new byte[1024];
int len;
while ((len=is.read(buf))!=-1) {//批量读取,减少性能消耗
System.out.println(new String(buf,0,len));
}
int len;
while ((len=is.read(buf))!=-1) {//批量读取,减少性能消耗
System.out.println(new String(buf,0,len));
}
如果很小,不建议使用循环,在TCP通信中会出错
客户端发送后,服务器端会进入读取,直到客户关闭流(os.close),否则一直阻塞
但客户如果在os.write后,进行os.close(),那么客户和服务器端的连接会断开
但客户如果在os.write后,进行os.close(),那么客户和服务器端的连接会断开
所以如果很小,直接去掉循环(只读取一次)
len = is.read(buf);
System.out.println(new String(buf,0,len));
len = is.read(buf);
System.out.println(new String(buf,0,len));
建议在每次os.write后直接os.flush()刷新输出流
Java JDBC
执行SQL语句的Java API
API的关系
|→Driver1 ——→ |→数据库(如MySQL) ——|
用户→DriverManager(调度中心)→Driver(司机)←数据库厂商→ |
|→Driver2——→ |→数据库(如Qracle) ——|
|———————————————————————————————————————————| ↓
Connection(会话通道) ←—用户与数据库连接成功后返回一个Connection对象
Statement/PreparedStatement(执行SQL语句的工具,Connection是它们的基础,在通道创建成功后起作用)
直接拼接SQL/预编译SQL ↓ ↓(会话通道)
用户 (SQL)→→→→→→→→→→→→→→→→→→→→→数据库(执行SQL并返回结果)
↓(会话通道) ↓
用户(ResultSet)返回查询结果←←←←←←←←←←←←←←←←←SQL查询类
(int)返回受影响行数←←←←←←←←←←←←←←←←←←←SQL更新类
, 无效SQL→抛出异常
用户→DriverManager(调度中心)→Driver(司机)←数据库厂商→ |
|→Driver2——→ |→数据库(如Qracle) ——|
|———————————————————————————————————————————| ↓
Connection(会话通道) ←—用户与数据库连接成功后返回一个Connection对象
Statement/PreparedStatement(执行SQL语句的工具,Connection是它们的基础,在通道创建成功后起作用)
直接拼接SQL/预编译SQL ↓ ↓(会话通道)
用户 (SQL)→→→→→→→→→→→→→→→→→→→→→数据库(执行SQL并返回结果)
↓(会话通道) ↓
用户(ResultSet)返回查询结果←←←←←←←←←←←←←←←←←SQL查询类
(int)返回受影响行数←←←←←←←←←←←←←←←←←←←SQL更新类
, 无效SQL→抛出异常
Driver:理论上可以直接使用Driver.connect()
但实际开发永远不需要直接调用,因为DriverManager已经封装了驱动匹配逻辑,更安全便捷
但实际开发永远不需要直接调用,因为DriverManager已经封装了驱动匹配逻辑,更安全便捷
DriverManager:根据用户的需求为其匹配合适的Driver
Conection:一个应用可以建立多个Connection对象(同时拨打多个电话)
代表一个用户与数据库的会话通道(独立:每一个Connection对象代表与一个不同的数据库的连接)
非线程安全,多个线程共享一个Connection可能混乱
代表一个用户与数据库的会话通道(独立:每一个Connection对象代表与一个不同的数据库的连接)
非线程安全,多个线程共享一个Connection可能混乱
Statement/PreparedManager:不是引导用户与数据库连接的作用,也不是给SQL指定路径的作用
在成功创建会话通道后起作用,只发送SQL(因为Connection通道已固定)
在成功创建会话通道后起作用,只发送SQL(因为Connection通道已固定)
ResultSet:返回,作用于数据库→用户,同样依赖于Connection通道
常用API
Driver(驱动程序接口)
Driver d = new
com.mysql.cj.jdbc.Driver();
org.h2.Driver();
DriverManager(驱动管理器接口)
常用“静态”方法
向DM注册给定的JDBC驱动程序
DriverManager.registerDriver(Driver d)
DriverManager.registerDriver(Driver d)
建立和数据库的连接,并返回表示连接的Connection对象
Connection c = DriverManager.getConnection(String url , String user , String pwd)
// JDBC URL 格式:jdbc:数据库类型://主机名:端口/数据库名?参数
// JDBC URL 格式:jdbc:数据库类型://主机名:端口/数据库名?参数
String url = "jdbc:mysql:……";
String user = "root";
String password = "123456";
H2
"jdbc:h2:mem:myBc"(自定义名称)
Connection(连接接口)
常用方法
创建一个Statement对象将SQL语句发送到数据库
PreparedStatement对象将参数化的SQL语句
(确保Statement对象绑定到正确的会话通道)
PreparedStatement对象将参数化的SQL语句
(确保Statement对象绑定到正确的会话通道)
Statement s = c.createStatement()
PreparedStatement ps = c.prepareStatement(String sql)
PreparedStatement ps = c.prepareStatement(String sql)
使用preparedStatement时,应该进行预编译
PreparedStatement ps = c.prepareStatement("INSERT INTO MyH2 (id, name) VALUES (?, ?)");
为每个占位符设置值(注意索引从1开始):
ps.setInt(1, 1); // 设置第一个参数(id)为整数1
ps.setString(2, "张三"); // 设置第二个参数(name)为字符串"张三"
ps.executeUpdate(); // 不需要再传入SQL,因为已经预编译了
ps.setInt(1, 1); // 设置第一个参数(id)为整数1
ps.setString(2, "张三"); // 设置第二个参数(name)为字符串"张三"
ps.executeUpdate(); // 不需要再传入SQL,因为已经预编译了
ps.setInt(1, 2);
ps.setString(2, "李四");
ps.executeUpdate();
ps.setString(2, "李四");
ps.executeUpdate();
要设置类型转换?问号
调用方法 对应Java类型 转换的数据库类型 示例
setString() String VARCHAR, TEXT, CHAR "北京" → '北京'
setInt() int INT, INTEGER 25 → 25
setDate() java.sql.Date DATE new Date(...) → '2023-10-01'
setBoolean() boolean BOOLEAN true → 1 (MySQL) / true (PostgreSQL)
setString() String VARCHAR, TEXT, CHAR "北京" → '北京'
setInt() int INT, INTEGER 25 → 25
setDate() java.sql.Date DATE new Date(...) → '2023-10-01'
setBoolean() boolean BOOLEAN true → 1 (MySQL) / true (PostgreSQL)
创建一个CallableStatement对象来调用数据库存储过程
CallableStatement cs = c.prepareCall(String sql)
Statement/PreparedStatement(陈述接口)
常用方法
Statement
判断获取查询结果
boolean x = s.execute(String sql)
若为true表示执行的SQL语句有查询结果
可通过Statement的getResultSet()获得查询结果
可通过Statement的getResultSet()获得查询结果
SQL更新类
int i = s.executeUpdate(Sting sql)
用于执行SQL中的insert\update\delete语句
返回的int值代表受语句影响的记录条数
返回的int值代表受语句影响的记录条数
s.executeUpdate("CREATE TABLE MyH2 (id INT, name VARCHAR(20))");
指定类型 varchar每一行的name列最多允许存储20个字符
指定类型 varchar每一行的name列最多允许存储20个字符
SQL查询类
ResultSet rs = s.executeQuery(Strig sql)
用于执行SQL中的select语句
返回的对象代表查询结果
返回的对象代表查询结果
直接拼接SQL,有SQL注入风险
PreparedStatement
SQL更新类
int i = ps.executeUpdate()
用于执行SQL语句,必须是一个DML语句或无返回内容的语句(DDL)
返回的int值代表受语句影响的记录条数
返回的int值代表受语句影响的记录条数
SQL查询类
ResultSet rs = ps.executeQuery()
用于执行SQL查询语句
返回的对象代表查询结果无参
返回的对象代表查询结果无参
可以防注入
如果已经在上方预编译,直接使用此语句即可
然后遍历即可
(ResultSet 没有实现Interable接口,
所以不能直接用于 for-each 循环)
(ResultSet 没有实现Interable接口,
所以不能直接用于 for-each 循环)
利用while和ResultSet的方法rs.next()
while(rs.next()) {
int id = rs.getInt("id");
String name = rs.getString("name");
System.out.println("ID: " + id + ", 姓名: " + name);
}
int id = rs.getInt("id");
String name = rs.getString("name");
System.out.println("ID: " + id + ", 姓名: " + name);
}
将指定参设置为给定int值
void = ps.setInt(1,30)
第一个占位符替换成30
将指定参设置成给定String值
void = ps.setString(2,"Aline")
第二个占位符替换成"Aline"
ResultSet(结果集接口)
常用方法
将游标从当前位置下移一行
boolean x = rs.next()
获取指定字段的int类型的值
int x = rs.getInt(int columnIndex)
(String columnName)
(String columnName)
获取指定字段的String类型的值
String x = rs.getString(int columnIndex)
(String columnName)
(String columnName)
释放资源
在文件末尾,关闭各种接口
rs.close();
queryPS.close();
ps.close();
s.close();
c.close();
queryPS.close();
ps.close();
s.close();
c.close();
SQL
列类型
SQL 类型 JDBC 获取方法 Java 类型
INT rs.getInt("列名") int
VARCHAR rs.getString("列名") String
FLOAT rs.getDouble("列名") double
DATE rs.getDate("列名") java.sql.Date
BLOB rs.getBlob("列名") Blob
INT rs.getInt("列名") int
VARCHAR rs.getString("列名") String
FLOAT rs.getDouble("列名") double
DATE rs.getDate("列名") java.sql.Date
BLOB rs.getBlob("列名") Blob
SQL类型:数据库表中的列定义的数据类型。
- JDBC获取方法:在ResultSet对象上调用哪种方法来获取该列的值。
- Java类型:获取方法返回的Java对象类型。
- JDBC获取方法:在ResultSet对象上调用哪种方法来获取该列的值。
- Java类型:获取方法返回的Java对象类型。
SQL 类型 JDBC 方法 Java 类型 说明
TINYINT getByte() byte 小整数
SMALLINT getShort() short 短整数
BIGINT getLong() long 大整数
DECIMAL getBigDecimal() BigDecimal 精确小数
BOOLEAN getBoolean() boolean 布尔值
TIME getTime() java.sql.Time 时间值
TIMESTAMP getTimestamp() java.sql.Timestamp 时间戳
CLOB getClob() Clob 文本大对象
ARRAY getArray() Array 数组类型
TINYINT getByte() byte 小整数
SMALLINT getShort() short 短整数
BIGINT getLong() long 大整数
DECIMAL getBigDecimal() BigDecimal 精确小数
BOOLEAN getBoolean() boolean 布尔值
TIME getTime() java.sql.Time 时间值
TIMESTAMP getTimestamp() java.sql.Timestamp 时间戳
CLOB getClob() Clob 文本大对象
ARRAY getArray() Array 数组类型
1. 数据定义语言(DDL - 创建/修改结构)
执行 DDL (CREATE TABLE, ALTER TABLE): 操作的是数据库的结构/元数据 (Metadata)
返回值通常是 0 或状态码
推荐使用 Statement。
执行 DDL (CREATE TABLE, ALTER TABLE): 操作的是数据库的结构/元数据 (Metadata)
返回值通常是 0 或状态码
推荐使用 Statement。
创建数据库对象
CREATE
数据库(DATABASE):创建一个新的数据库
表(TABLE):创建一个新的表结构(包括定义列)。
定义列的类型
s.executeUpdate("CREATE TABLE MyH2 (id INT, name VARCHAR(20))");
指定类型 varchar每一行的name列最多允许存储20个字符
指定类型 varchar每一行的name列最多允许存储20个字符
视图(VIEW):创建一个基于查询的虚拟表。
索引(INDEX):为表中的列创建索引以提高查询速度
存储过程(PROCEDURE) / 函数(FUNCTION):创建存储在数据库中的程序代码单元
触发器等(TRIGGER):创建在表发生特定事件时自动执行的代码
修改现有结构
ALTER
表(TABLE):修改表结构(添加/删除列)。
ALTER TABLE 表名称 ADD COLUMN 新列名 数据类型 [约束条件]
DROP
DROP
其他大部分对象功能受限
删除数据库对象
DROP
CREATE支持的对象DROP都支持
2. 数据操作语言(DML - 操作数据)⭐ 重点
执行 DML (INSERT, UPDATE, DELETE): 操作的是表里的具体数据行 (Records)
返回值是受影响的行数
强烈推荐使用 PreparedStatement!
执行 DML (INSERT, UPDATE, DELETE): 操作的是表里的具体数据行 (Records)
返回值是受影响的行数
强烈推荐使用 PreparedStatement!
添加数据
INSERT
INSERT INTO 表名 (列1, 列2, ...) VALUES (值1, 值2, ...);
String sql = "INSERT INTO users (name, age, email) VALUES (?, ?, ?)";
ps.executeUpdate("INSERT INTO MyH2(id,name) VALUES (1,'张三')");
修改数据
UPDATE
UPDATE 表名 SET 列1=新值1, 列2=新值2, ... [WHERE 条件];
-- 无WHERE会更新整张表!
String sql = "UPDATE users SET email = ? WHERE id = ?";
删除数据
DELETE
DELETE FROM 表名 [WHERE 条件];
-- 无WHERE会删除整张表!
String sql = "DELETE FROM users WHERE id = ?";
查询数据
SELECT
SELECT 列1, 列2, ... FROM 表名
[WHERE 条件]
[ORDER BY 排序字段]
[LIMIT 数量];
[WHERE 条件]
[ORDER BY 排序字段]
[LIMIT 数量];
[ORDER BY 排序字段]用于对查询结果按照一个或多个列进行排序。
可以指定升序(ASC)或降序(DESC)。默认是升序(ASC)
可以指定升序(ASC)或降序(DESC)。默认是升序(ASC)
ORDER BY age DESC, salary DESC
第一优先级 第二优先级
第一优先级 第二优先级
多列排序 = 先按第一列分组 → 组内按第二列排序 → 保持行数据完整
如果age列没有重复值,那么第salary不会触发
如果age有重复值,那么俩个重复的值会进行salary判断
如果age有重复值,那么俩个重复的值会进行salary判断
age(第一优先级)排序会带动整一行排序
[LIMIT 数量]用于限制查询结果返回的行数。
它通常用于分页或者只获取前几行记录
它通常用于分页或者只获取前几行记录
`*` 是通配符,表示选择表中所有列
LIMIT [offset,] row_count;
row_count:指定返回的最大行数。
offset:可选参数,表示跳过多少行开始返回(偏移量)。默认偏移量是0(即从第一行开始)。
row_count:指定返回的最大行数。
offset:可选参数,表示跳过多少行开始返回(偏移量)。默认偏移量是0(即从第一行开始)。
只返回前5条记录:SELECT * FROM users LIMIT 5;
-跳过前10条记录,然后返回接下来的5条记录(常用于分页):SELECT * FROM users LIMIT 10, 5;
-跳过前10条记录,然后返回接下来的5条记录(常用于分页):SELECT * FROM users LIMIT 10, 5;
String sql = "SELECT id, name, age FROM users WHERE age > ?";
3. 数据控制语言(DCL - 权限管理)
推荐使用 Statement。
推荐使用 Statement。
授予权限
GRANT
GRANT SELECT ON students TO user1
撤销权限
REVOKE
REVOKE DELETE ON students FROM user1
GRANT 权限列表 ON 对象名 TO 用户名/角色名; -- 授予权限
REVOKE 权限列表 ON 对象名 FROM 用户名/角色名; -- 撤销权限
REVOKE 权限列表 ON 对象名 FROM 用户名/角色名; -- 撤销权限
对象名常为表名,也可以是数据库,索引……
4.执行查询 (SELECT): 操作的是已有的数据
是在读取数据库里已经存储好的信息。ResultSet 接收的是数据内容。
见上方Statement/PreparedStatement(陈述接口)常用方法或2DML
见上方Statement/PreparedStatement(陈述接口)常用方法或2DML
结构简图
数据库
表
列(id) 列(name) ……
行 1 张三
行
……
行 1 张三
行
……
列(Column):是在创建表(CREATE TABLE)时定义的,
也(只能)可以使用ALTER TABLE语句来添加或修改列
也(只能)可以使用ALTER TABLE语句来添加或修改列
行(Row):是表中的数据记录,使用INSERT语句来添加,不能使用CREATE语句来创建行
表
CREATE TABLE (创建表)
……
Java GUI
图形用户界面
Swing组件(以AWT为基础的轻量级组件)
javax.swing
javax.swing
Container(顶级父类)
AWT的Window类
容器分支(继承win类)
JWindow
Frame
JFrame顶级容器
(非模态)
(非模态)
构造方法
JFrame jf = new JFrame("JFrame测试");
通过构造方法创建实例,然后通过对象调用
这个更好,更简单
这个更好,更简单
class Test extends JFrame{
可以直接调用方法,而不需要实例对象
size():
}
可以直接调用方法,而不需要实例对象
size():
}
可以选择是否设置标题
普通方法
void =
(推荐按这排列顺序设置)
(推荐按这排列顺序设置)
设置窗口标题
jf.setTitle(String x)
设置窗口大小
jf.setSize(w,h);
(Dimension d );
(Dimension d );
用于精确控制窗口尺寸
Dimension d = new Dimension(300,300);
Dimension d = new Dimension(300,300);
设置窗口位置
jf.setLocation(x,y);建议与宽高搭配计算
(Point p);
(Point p);
Point p = new Point(x,y);
使用匿名对象调用不如直接设置参数,使用对象是为了方便批量操作
使用匿名对象调用不如直接设置参数,使用对象是为了方便批量操作
if.setLocationRelativeTo(Component c)
窗口相对于哪个组件居中显示
设置为null时,窗口相对于屏幕居中!
设置为null时,窗口相对于屏幕居中!
自动设置窗口
jf.pack()
通常在代码最后设置,自动调整窗口大小,以适合子组件首选大小和布局
替代setSize手动设置,同时设置时,pack会覆盖setSize
替代setSize手动设置,同时设置时,pack会覆盖setSize
▲设置窗口显示与否
jf.setVisible(boolean b)
true为显示
设置窗口背景颜色
jf.getContentPane().setBackground(Color c);
(new Color(R,G,B))
(new Color(R,G,B))
JF的内容由JRootPane管理
if.setBackground(Color c);
直接调用无效
设置用户关闭窗口时的默认行为
if.setDefaultCloseOperation(int operation)
默认
JFrame.HIDE_ON_CLOSE(关闭窗口时隐藏窗口)
JFrame.EXIT_ON_CLOSE(关闭窗口时终止整个JVM进程)
JFrame.DISPOSE_ON_CLOSE(关闭窗口时释放窗口资源,不终止JVM)
Jframe.DO_NOTHING_ON_CLOSE(关闭窗口时,不执行任何操作)
销毁窗口,并释放关联资源
jf.dispose()
适用于 提示框
设置布局管理器
(推荐通过内容面板(默认是Jpanel)设置)
(推荐通过内容面板(默认是Jpanel)设置)
cp.setLayout(LayoutManager mgr)
设置的是对象(null除外)
设置的是对象(null除外)
(默认)BorderLayout
null(绝对布局)
组件需要手动设置位置大小
(硬编码尺寸,不好用!)
(硬编码尺寸,不好用!)
详见下方布局管理器
▲获取内容面板
(默认是Jpanel)
(默认是Jpanel)
Container cp = jf.getContentPane()
获取内容面板,组件并不是直接放置在JFrame上的,与组件直接交互的是内容面板x
该方法能将窗体jf转换为容器x,但窗体依旧存在为jf可以被操作
该方法能将窗体jf转换为容器x,但窗体依旧存在为jf可以被操作
可以不写JPanel,是因为父类引用指向子类,JPanel是Container的子类
添加组件
(推荐通过内容面板(默认是Jpanel)设置)
(推荐通过内容面板(默认是Jpanel)设置)
Component x = cp.add(Component comp)
添加一个组件,并返回对应该组件的对象
(通常操作该对象本身comp来设置属性,而不是返回的对象x)
(通常操作该对象本身comp来设置属性,而不是返回的对象x)
不推荐组件匿名对象调用添加,除非这个按钮没任何作用,从创建开始就不会改变
创建和显示GUI的标准且推荐的方式
SwingUtilities.invokeLater(JFrameTest::newUi)
newUi方法所在类 JFrame所在的方法
newUi方法所在类 JFrame所在的方法
推荐public static void newUI()方法在设置主类内,在main方法内调用SUiL()
JDialog不需要,只需要绑定好JF对象即可
Dialog
JDialog顶级容器
(模态:阻塞父窗口)
(非模态:不阻塞父窗口)
(模态:阻塞父窗口)
(非模态:不阻塞父窗口)
构造方法
JDialog jd = new JDialog(jf)指定jd的所有者为jf
(jf , "JDialog测试")jf,jd的标题(非模态)
(jf , boolean modal )jf,true(模态)(无标题)
(jf , "JDialog测试", boolean modal )if,jd标题,是否模态
或Dialog.ModalityType
(jf , "JDialog测试")jf,jd的标题(非模态)
(jf , boolean modal )jf,true(模态)(无标题)
(jf , "JDialog测试", boolean modal )if,jd标题,是否模态
或Dialog.ModalityType
普通方法(几乎都跟JFrame相同)
只需注意模态行为及其默认关闭模式即可
只需注意模态行为及其默认关闭模式即可
void =
(推荐按这排列顺序设置)
(推荐按这排列顺序设置)
设置窗口标题
jd.setTitle(String x)
设置窗口大小
jd.setSize(w,h);
(Dimension d );
(Dimension d );
用于精确控制窗口尺寸
Dimension d = new Dimension(300,300);
Dimension d = new Dimension(300,300);
设置窗口位置
jd.setLocation(x,y);建议与宽高搭配计算
(Point p);
(Point p);
Point p = new Point(x,y);
使用匿名对象调用不如直接设置参数,使用对象是为了方便批量操作
使用匿名对象调用不如直接设置参数,使用对象是为了方便批量操作
jd.setLocationRelativeTo(Component c)
窗口相对于哪个组件居中显示
设置为绑定得对象时,窗口相对于绑定对象居中!
设置为绑定得对象时,窗口相对于绑定对象居中!
自动设置窗口
jd.pack()
通常在代码最后设置,自动调整窗口大小,以适合子组件首选大小和布局
替代setSize手动设置,同时设置时,pack会覆盖setSize
替代setSize手动设置,同时设置时,pack会覆盖setSize
设置窗口背景颜色
jd.getContentPane().setBackground(Color c);
(new Color(R,G,B))
(new Color(R,G,B))
JD的内容由JRootPane管理
jd.setBackground(Color c);
直接调用无效
▲设置窗口显示与否
jd.setVisible(boolean b)
true为显示
设置用户关闭窗口时的默认行为
jd.setDefaultCloseOperation(int operation)
JDialog.HIDE_ON_CLOSE(关闭窗口时隐藏窗口)
JDialog.EXIT_ON_CLOSE(关闭窗口时终止整个JVM进程)
默认
JDialog.DISPOSE_ON_CLOSE(关闭窗口时释放窗口资源,不终止JVM)
JDialog.DO_NOTHING_ON_CLOSE(关闭窗口时,不执行任何操作)
销毁窗口,并释放关联资源
jd.dispose()
适用于 提示框
设置布局管理器
(推荐通过内容面板(默认是Jpanel)设置)
(推荐通过内容面板(默认是Jpanel)设置)
cp.setLayout(LayoutManager mgr)
设置的是对象(null除外)
设置的是对象(null除外)
(默认)BorderLayout
null(绝对布局)
组件需要手动设置位置大小
(硬编码尺寸,不好用!)
(硬编码尺寸,不好用!)
详见下方布局管理器
▲获取内容面板
Container cp = jd.getContentPane()
获取内容面板,组件并不是直接放置在JDialog上的,与组件直接交互的是内容面板x
该方法能将窗体jd转换为容器x,但窗体依旧存在为jd可以被操作
该方法能将窗体jd转换为容器x,但窗体依旧存在为jd可以被操作
添加组件
(推荐通过内容面板(默认是Jpanel)设置)
(推荐通过内容面板(默认是Jpanel)设置)
Component x = cp.add(Component comp)
添加一个组件,并返回对应该组件的对象
(通常操作该对象本身comp来设置属性,而不是返回的对象x)
(通常操作该对象本身comp来设置属性,而不是返回的对象x)
不推荐组件匿名对象调用添加,除非这个按钮没任何作用,从创建开始就不会改变
与模态相关的核心方法
void =
jd.setModal(blooean modal)
true:模态,阻塞父窗口
false:非模态,不阻塞父窗口
jd.setModalityType()
MODELESS(非模态)
APPLICATION_MODAL(应用程序级模态,阻塞JVM所有窗口(除JDialog本身及组件))
DOCUMENT_MODAL(文档级模态,仅阻塞与对话框(JDialog)关联的顶级窗口)
TOOLKIT_MODAL(工具包级模态,阻塞JVM所有窗口,与应用级类型,行为可能因平台不同)
boolean x = jd.isModal()
true:模态
false:非模态
false:非模态
窗口组件
JPanel,JScollPane即使组件也是窗口
组件分支
JComponent(公共超类)
所有子类继承其所有公共方法
所有子类继承其所有公共方法
面板组件
(中间容器)
(中间容器)
中间容器的设置布局管理器,向其添加组件或中间容器的操作,都必须通过中间容器的自身对象。因为它既是容器也是组件
Container类
(内容面板类)
(内容面板类)
JPanel
无边框且不可操作
构造方法
JPanel jp = new JPanel();
(new BorderLayout());默认是FlowLayout
(new BorderLayout());默认是FlowLayout
JScrollPane
滚动条,单组件容纳(若想要多组件,应该将JPanel作为该容器的单组件)
构造方法
JScollPane jsp = new JScollPane();
(Component view); 显示指定组件(组件内容超过窗口时显示滚动条)
(Component view , int vsbPolicy , int hsbPolicy); ······,垂直滚动条策略,水平滚动条策略
(Component view); 显示指定组件(组件内容超过窗口时显示滚动条)
(Component view , int vsbPolicy , int hsbPolicy); ······,垂直滚动条策略,水平滚动条策略
方法
void =
设置水平滚动条
jsp.setHorizontalBarPolicy(int policy)
设置垂直滚动条
jsp.setVerticalBarPolicy(int policy)
设置面板组件
jsp.setViewportView(Component view)
单组件容器,不然添加多个组件,不然只有最后一个会生效该规则
滚动属性
int常量policy
默认值
HORIZONTAL_SCROLLBAR_AS_NEEDED
VERTICAL
VERTICAL
始终显示
HORIZONTAL_SCROLLBAR_ALWAYS
VERTICAL
VERTICAL
始终不显示
_NEVER
文本组件
JTextComponent类
子类
JTextField
文本框,只能接收单行文本输入
构造方法
JTextField jtf = new JTextField(); 初始字符串为""
(int cols); 初始字符串为"",指定文本框列数
(String text); 初始字符串为text
(String text , int cols); 初始字符串为text,指定文本框列数
(int cols); 初始字符串为"",指定文本框列数
(String text); 初始字符串为text
(String text , int cols); 初始字符串为text,指定文本框列数
常用方法
jtx.setFont(new Font("微软雅黑", Font.PLAIN, 22));
jtx.setBorder(BorderFactory.createLineBorder(Color.BLACK));//边框
jtx.setLineWrap(true); // 启用自动换行
jtx.setBorder(BorderFactory.createLineBorder(Color.BLACK));//边框
jtx.setLineWrap(true); // 启用自动换行
jtx.append(String x);//添加文本
jtx.setText(String x);//设置文本
jtx.setTitle(String x);设置标题
jtx.setText(String x);//设置文本
jtx.setTitle(String x);设置标题
JTextArea
文本域,能接收多行文本的输入
构造方法
JTextArea jta = new JTextArea(); 初始字符串为""
(int rows , int cols); 初始字符串为"",指定文本框行数,列数
(String text); 初始字符串为text
(String text , int rows , int cols); 初始字符串为text,指定文本框行数,列数
(int rows , int cols); 初始字符串为"",指定文本框行数,列数
(String text); 初始字符串为text
(String text , int rows , int cols); 初始字符串为text,指定文本框行数,列数
常用方法
String x =
jt.getText()
返回组件所有文本内容
jt.getSelectedText()
返回组件选定的文本内容
void =
jt.selectAll()
选中组件所有内容
jt.setEditable()
设置组件的可否编辑
jt.setText(String text)
设置组件的内容
jt.replaceSelection(String content)
用content替换当前选中的内容
标签组件
JLabel
仅供展示,可以显示文本,图像
获取分层面板底层(默认是JPanel),再往其中添加JLabel,在调用jl.setIcon(Image icon)
ImageIcon(特殊类用户封装数据)
JLabel label = new JLabel();
ImagwIcon icon = new ImageIcon("Pic.png");//表示图片路径位于项目根目录
Image img = icon.getImage();//将返回值赋值为对象img
img = img.getScaledInstance(300,150,Image.SCALE_DEFAULT);//设置图像宽,高,较平衡的缩放处理(推荐与窗口大小相同)
icon.setImage(img);//将设置完图像属性的对象赋值给icon
label.setIcon(icon);//将icon添加到JLabel组件中
构造方法
JLabel jl = new JLabel();
(Icon image); 将ImageIcon的对象作为参数
(icon image , int align); 额外指定水平的对齐方式
(String text); 指定文本内容
(String text , Icon icon , int align); 文本内容,图像,水平对齐方式
(String text , int align); 文本内容,水平对齐方式
(Icon image); 将ImageIcon的对象作为参数
(icon image , int align); 额外指定水平的对齐方式
(String text); 指定文本内容
(String text , Icon icon , int align); 文本内容,图像,水平对齐方式
(String text , int align); 文本内容,水平对齐方式
int align = JLabel.CENTER
JLabel.LEFT
JLabel.RIGHT
JLabel.LEFT
JLabel.RIGHT
按钮组件
AbstractButton
子类
JButton
常用于事件处理
构造方法
JButton jb = new JButton("按钮")
循环创造按钮数组
然后按索引添加
// 数字按钮数组 (0-9)
JButton[] numButtons = new JButton[10];
for (int i = 0; i <= 9; i++) {
numButtons[i] = new JButton(String.valueOf(i));
}
// 运算符按钮数组 (按行顺序)
String[] operatorSymbols = {"/", "*", "+", "-"};
JButton[] opButtons = new JButton[operatorSymbols.length];
for (int i = 0; i < operatorSymbols.length; i++) {
opButtons[i] = new JButton(operatorSymbols[i]);
}
// 特殊按钮
JButton decimalBtn = new JButton(".");
JButton equalsBtn = new JButton("=");
for(JButton num :numButtons){
num.addActionListener(e->{})
}
遍历并绑定事件
num.addActionListener(e->{})
}
遍历并绑定事件
可以在事件中使用num.getText()来判断是哪个按钮
JCheckBox
复选框,可以选中一个或多个
构造方法
JCheckBox jcb = new JCheckBox(); 空白按钮
(String x); 带文本的按钮
(String x , boolean selected); 带文本且指定状态(是否选中)的按钮
(String x); 带文本的按钮
(String x , boolean selected); 带文本且指定状态(是否选中)的按钮
JRadioButton
单选框,利用不可见的ButtonGroup组件达成
ButtonGroup bg = new ButtonGroup();
bg.add(jrb);
bg.add(jrb1);
bg.add(jrb);
bg.add(jrb1);
构造方法
JRadioButton jrb = new JRadioButton(); 空白按钮
(String x); 带文本的按钮
(String x , boolean selected); 带文本且指定状态(是否选中)的按钮
(String x); 带文本的按钮
(String x , boolean selected); 带文本且指定状态(是否选中)的按钮
常用方法
(常用于事件绑定)
(常用于事件绑定)
返回按钮的状态(选中ture)
boolean x = jb.isSelected();
设置
设置按钮图标
void = jb.setIcon(Icon x);
设置按钮的文本
void = jb.setText(String x);
设置按钮是否可用
void = setEnable(boolean x);
设置按钮是否为选中状态
boolean x = jb.setSelected(boolean x);
获取
获取按钮图标
Icon x = jb.getIcon();
获取按钮文本
String x = jb.getText();
下拉框组件
JComboBox(下拉框)
折叠选项,默认显示第一个。有可编辑和不可编辑状态,编辑状态下用户输入的内容不会添加到选项中
构造方法
JComboBox jcb =new JComboBox(); 无可选项下拉框
(Object[] items); 以集合中的元素
(Vector[] items); 为选项的下拉框
(Object[] items); 以集合中的元素
(Vector[] items); 为选项的下拉框
常用方法
添加
添加选项
void = jcb.add(Object x);
指定索引添加
void = jcb.insertItemAt(Object x , int index);
删除
删除所有选项
void = jcb.removeAllItems();
aLLit
删除指定选项
void = jcb.removeItem(Object x);
删除指定索引选项
void = jcb.removeItemAt(int index);
获取
返回指定索引处的对象
Object x = jcb.getItemAt(int index);
返回选项的数目
int x = jcb.getItemCount();
返回当前所选项
int x = jcb.getSelectedItem();
设置
设置可否编辑状态(true可编辑)
void = jcb.setEditable(boolean x);
JMenuBar(菜单栏)
水平菜单栏,不交互,用于管理一组菜单,通常使用顶级容器(JF,JD)的setJMenuBar(jmb)设置位置
是独立于内容面板的
构造方法
JMenuBar jmb = new JMenuBar()
jmb.add(JMenu jm )
JMenu(菜单)
构造方法
JMenu jm = new JMenu("编辑");
方法
大多用于操作菜单中的菜单项
JMenuItem(菜单项)
继承自AbstractButton类,常使用父类的setText()和setIcon()
构造方法
JMenuItem jmi = new JMenuItem("新建文件");
后面是事件处理
添加
添加菜单项到菜单末尾并返回
JMenuItem jmi1 = jm.add(new JMenuItem("选项1"));
添加分隔符到菜单末尾
void = jm.addSeparator();
获取
返回菜单的项数(菜单项和分隔符)
int x = jm.getItemCount();
返回指定索引的菜单项
JMenuItem x = jm.getItem(int index);
删除
删除指定菜单项
void = jm.remove(jmi);
删除指定索引的菜单项
void = jm.remove(int index);
删除所有菜单项
void = jm.removeAll();
修改
指定索引插入
菜单项
JMenuItem x = jm.insert(Jmi , int index);
分隔符
void = jm.insertSeparator(int index);
JPopupMenu(弹出式菜单)
右键单击弹出(事件绑定),默认不可见,同样使用JMI菜单项
构造方法
JPopupMenu jpm = new JPopupMenu();
方法
显示(常用于事件绑定)
show(Component x ,int x, int y); 参考组件,x,y菜单左上角的坐标
添加选项
JMenuItem jmi = jpm.add(new JMenuItem("选项1"));
组件设置
字体(大小,类型)
jtx.setFont(new Font("微软雅黑", Font.PLAIN, 22));
Font.BOLD | Font.ITALIC 表示粗斜体)。PLAIN 表示不应用任何额外的加粗或倾斜效果。
组件大小(不想要最佳大小时)
jtf.setPreferredSize(new Dimension(300, 40));//修改最佳大小
边框(多样化)
jtx.setBorder(BorderFactory.createLineBorder(Color.BLACK));//边框
定义方法复用样式
在期末作业时一些文件里面大量组件使用相同的三个样式代码导致冗杂
布局管理器
决定组件的位置和尺寸
实现效果
FlowLayout
格子
无格
行高由最高组件决定(组件又是最佳大小,人话就是不用操心去设置)
组件添加
按照添加顺序从左到右
组件尺寸
组件使用最佳大小
格与组件
默认居中以最佳大小显示
BorderLayout
格子
南北高不变,东西宽不变,中间都改变
窗口大小改变时,对应区域的行为
用户调整窗口大小,组件的相对位置不变
一个区域只能放置一个组件,旧的会被新的覆盖
组件添加
添加时指定添加的区域(东西南北中)
组件尺寸
NORTH和SOUTH:组件的宽度被拉伸至容器宽度,高度保持最佳高度。
EAST和WEST:组件的高度被拉伸(在除去NORTH和SOUTH后的高度),宽度保持最佳宽度。
CENTER:组件的宽度和高度都被拉伸以填满剩余空间。
EAST和WEST:组件的高度被拉伸(在除去NORTH和SOUTH后的高度),宽度保持最佳宽度。
CENTER:组件的宽度和高度都被拉伸以填满剩余空间。
如果不想组件填满区域,可以将组件添加JPanel(FlowLayout)
再将JPanel添加到BorderLayout
再将JPanel添加到BorderLayout
格与组件
一个区域一个组件
指向同一区域的组件,新组件会覆盖旧组件
GridLayout
格子
按指定的行列将窗口平均分割成等大的格子
组件添加
按照添加顺序从左到右
组件尺寸
默认撑满整个格子
格与组件
一个格子只能有一个组件
若组件超过总格数,会自动扩行(列不变)
GridBagLayout
(最复杂)
(最复杂)
格子
动态网格
具体看行列中有 几个组件,且每个组件跨了几格
组件添加
可以设置索引将组件放在想要的位置,如果不设置索引则跟GL一致
组件尺寸
默认保持最小
格与组件
格子与组件是相对的,不会像BL那样只有五个区域又不会自动扩展,也不像GL只能扩行
跟动态 网格一块变化以容纳组件
跟动态 网格一块变化以容纳组件
属性理解
weightx/y
权重,一旦设置非默认值(非0),则所有组件容器总是占满整个窗口
(不是组件)
(不是组件)
每一行的权重只生效一个,那就是最大的weighty
每一列的权重只生效一个,那就是最大的weightx
代码构造
FlowLayout(流式布局管理器)
简单布局
简单布局
构造方法
(一般使用匿名对象调用
作为setLayout的参数)
(一般使用匿名对象调用
作为setLayout的参数)
new FlowLayout()
(布局常量)
(布局常量,int hgsp,int vgap)
(布局常量)
(布局常量,int hgsp,int vgap)
默认居中对齐,水平垂直间距默认为5个单位
指定组件对齐方式,水平垂直间距默认为5个单位
指定组件的对齐方式,水平垂直间距
指定组件对齐方式,水平垂直间距默认为5个单位
指定组件的对齐方式,水平垂直间距
布局常量
(static final int)
静态全局,不可变,基本类型
(static final int)
静态全局,不可变,基本类型
居中对齐
FlowLayout.CENTER
左对齐
FlowLayout.LEFT
右对齐
FlowLayout.RIGHT
与容器开始端对齐方式一样
FlowLayout.LEADING
取决于容器组件方向
ComponentOrientation.LEFT_TO_RIGHT
开始端为左
ComponentOrientation.LEFT_TO_RIGHT
开始端为左
组件添加
container(容器对象).add(Component comp)
组件
组件
BorderLayout(边界布局管理器)
复杂布局
复杂布局
构造方法
(一般使用匿名对象调用
作为setLayout的参数)
(一般使用匿名对象调用
作为setLayout的参数)
new BorderLayout()
(int hgsp,int vgap)
(int hgsp,int vgap)
没有边距的布局器
有水平垂直边距的布局器(防止组件堆一块)
有水平垂直边距的布局器(防止组件堆一块)
布局常量
(static final String)
静态全局,不可变,基本类型
(static final String)
静态全局,不可变,基本类型
东
BorderLayout.EAST
西
BorderLayout.WEST
南
BorderLayout.SOUTH
北
BorderLayout.NORTH
默认
中
BorderLayout.CENTER
组件添加
container(容器对象).add(Component comp , Object constraints)
组件 约束
组件 约束
add(new JButton("北"), BorderLayout.NORTH)
GridLayout(网格布局管理器)
美观布局
美观布局
效果
将窗口按指定的行数和列数平均分割
构造方法
(一般使用匿名对象调用
作为setLayout的参数)
(一般使用匿名对象调用
作为setLayout的参数)
new GridLayout()
(int rows, int cols)
(int rows, int cols , int hgsp,int vgap)
(int rows, int cols)
(int rows, int cols , int hgsp,int vgap)
默认一行,一个组件一列
指定行数和列数
指定行数列数,组件的间距
指定行数和列数
指定行数列数,组件的间距
组件添加
container(容器对象).add(Component comp)
组件
组件
GridBagLayout(网格包布局管理器)
自由布局
自由布局
使用
获取内容面板contanier
1.GridBagLayout设置布局管理器类型
2.GridBagConstraints设置布局属性
3.GBL通过方法将GBC的布局属性绑定到组件上
2.GridBagConstraints设置布局属性
3.GBL通过方法将GBC的布局属性绑定到组件上
将按钮添加到内容面板
创建方法
(自由布局,需要对象来设置)
(自由布局,需要对象来设置)
设置布局管理器
GridBagLayout gbl = new GridBagLayout();
container.setLayout(gbl);
container.setLayout(gbl);
container由对应窗口(JF,JD)获取,Container container = JF(D).getContentPane()
用于设置属性
GridBagConstraints gbc = new GridBagConstraints();
绑定组件和属性
gbl.setConstraints(组件, gbc)
JButton button1 = new JButton("Button 1");
gbc.gridx = 0;
gbc.gridy = 0;
gbl.setConstraints(button1, gbc); // 将button1和约束gbc关联
container.add(button1);
gbc.gridx = 0;
gbc.gridy = 0;
gbl.setConstraints(button1, gbc); // 将button1和约束gbc关联
container.add(button1);
组件添加
container(容器对象).add(Component comp)
组件 U
组件 U
GBC属性
设置组件的索引(组件的位置,在第几行,第几列)
gbc.gridx = 0
gbc.gridy = 0
若设置为默认值:GridBagContraints.RELATIVE,组件默认紧跟上一个组件后面
设置组件占的格子数(跨越)
即使不表现出来,但实际是生效的,你设置了gridwidth从1到5,那么按钮就从水平只占一格到水平占了5格
gbc.gridwidth = 1
gbc.gridheight = 1
默认值都是1
特殊常量值(用于换行)
gbc.gridwidth = GridBagConstraints.REMAINDER
占据一行中剩下的所有列
再设置gbc.gridheight = 1可以达到换行效果
gbc.gridheight = GridBagConstraints.REMAINDER
占据一列中的所有行
组件与容器的相对大小
gbc.weightx = 0
gbc.weighty = 0
默认值是0,即不去占用多余空间
若水平三个组件,分别设置1,2,3。那么窗口宽度增加60px时,3个容器分别增加10px,20px,30px
1,0,0 60px,0px,0px
1,0,0 60px,0px,0px
组件大小的改变方式
GBL的组件不像GL一样占满整个格子,而是默认保持最小
(在下面的整理会对比)
GBL的组件不像GL一样占满整个格子,而是默认保持最小
(在下面的整理会对比)
gbc.fill =
GridBagConstraints.NONE:默认,不改变
GridBagConstraints.HORIZONTAL:使组件水平足够长填满宽,高度不变
GridBagConstraints.VERTICAL:使组件垂直足够长填满高,宽度不变
GridBagConstraints.BOTH:填满整个区域
组件的对齐方式
(太多,知道有这个属性即可)
(太多,知道有这个属性即可)
绝对位置
gbc.anchor = GridBagConstraints.CENTER(默认)
居中对齐就可以满足99%的情况了,又是默认的,所以知道有该属性便可
相对位置
事件处理
推荐适配器类,或匿名内部类实现,如果显示定义类实现接口,不好对多个组件进行操作
只实现某个方法 必须实现接口所有方法(在下方进行方法示例)
在下方单独分支讲解
只实现某个方法 必须实现接口所有方法(在下方进行方法示例)
在下方单独分支讲解
适配器
// 空实现适配器(抽象类)
public abstract class FileOperationAdapter implements FileOperationListener {
@Override public void onCreated(String fileName) {} // 空实现
@Override public void onModified(String fileName) {} // 空实现
@Override public void onDeleted(String fileName) {} // 空实现
@Override public void onRenamed(String oldName, String newName) {} // 空实现
}
// 业务类:只关心删除操作
public class DeleteLogger extends FileOperationAdapter {
@Override
public void onDeleted(String fileName) {
System.out.println("文件被删除: " + fileName);
}
// 其他方法无需重写(自动继承空实现)
}
1. 命名规范:适配器类名建议使用XXXAdapter格式(如 KeyAdapter)
2. 抽象类:适配器应声明为 abstract(抽象类) (虽然技术上非必须,但明确设计意图)
3. 避免过度使用:当接口方法较少时(<3个),直接实现接口可能更简洁
类型
WindowEvent类(窗体事件)
WindowListener接口
new WindowListener(){
public void windowOpened(WindowEvent e){
windowIconified
windowDeiconified
windowDeactivated
windowClosing
windowClosed
windowActivated
}
}
public void windowOpened(WindowEvent e){
windowIconified
windowDeiconified
windowDeactivated
windowClosing
windowClosed
windowActivated
}
}
窗口打开事件
图标化
取消图标化
停用
正在关闭
关闭
激活
图标化
取消图标化
停用
正在关闭
关闭
激活
组件.addWindowListener(参数为匿名内部类)
MouseEvent类(鼠标事件)
MouseListener接口
new MouseListener(){
public void mouseReleased(MouseEvent e){
mousePressed
mouseExited
mouseEntered
public void mouseClicked(MouseEvent e){
if(e.getButton()== MouseEvent.BUTTON1){
System.out.print("鼠标左键单机事件");
}
if(e.getButton()==MouseEvent.BUTTON2){
System.out.print("鼠标中键单击事件")
}
if(e.getButton()==MouseEvent.BUTTON3){
System.out.print("鼠标右键单机事件")
}
}
}
}
public void mouseReleased(MouseEvent e){
mousePressed
mouseExited
mouseEntered
public void mouseClicked(MouseEvent e){
if(e.getButton()== MouseEvent.BUTTON1){
System.out.print("鼠标左键单机事件");
}
if(e.getButton()==MouseEvent.BUTTON2){
System.out.print("鼠标中键单击事件")
}
if(e.getButton()==MouseEvent.BUTTON3){
System.out.print("鼠标右键单机事件")
}
}
}
}
鼠标放开事件
按下
移出按钮区域
进入按钮区域
单击
按下
移出按钮区域
进入按钮区域
单击
组件.addMouseListener();
MouseEvent定义了常量来识别操作,所以在mouseClicked方法中表述出来
int btn = e.getButton(),用户单击左键时返回1
int btn = e.getButton(),用户单击左键时返回1
KeyEvent类(键盘事件)
KeyListener接口
new KeyListener(){
public void keyPressed(KeyEvent e)
char keyChar = e.getKeyChar();//获取用户按下的字符
int keyCode = e.getKeyCode();//获取用户按下的字符对应的编码
}
public void keyPressed(KeyEvent e)
char keyChar = e.getKeyChar();//获取用户按下的字符
int keyCode = e.getKeyCode();//获取用户按下的字符对应的编码
}
组件.addKeyListener();
ActionEvent类(动作事件)
ActionListener接口
new ActionListener(){
public void actionPerformed(ActionEvent e){
jf.dipose();
}
}
public void actionPerformed(ActionEvent e){
jf.dipose();
}
}
l推荐Lambda语句,因为动作事件是单方法接口
通过绑定的按钮,我们希望它的行为来实现方法
例如绑定的是关闭的按钮,那它的行为就是关闭
通过绑定的按钮,我们希望它的行为来实现方法
例如绑定的是关闭的按钮,那它的行为就是关闭
组件.addActionListener();
AWT(中重量级组件)
java.awt
java.awt
本章不作学习
为整理
顶级容器(JFrame,JDialog)
new JFrame();new JDialog
new JFrame();new JDialog
内容面板(默认是JPanel)
Container cp = jf(jd).getContentPane()
Container cp = jf(jd).getContentPane()
上层JPanel
管理组件
Component x = cp.add()
下层JPanel
管理背景
getContentPane(ScollPane)
获取的就是JScollPane实例,而不是Jpanel
创建JPanel对象,重写paintComponent方法绘制背景
再通过jf.setContentPane(jp对象)
Java 反射机制
基础
getConstructor(Class<?>... parameterTypes)方法中的参数部分:Class<?>... paramTypes。
这里有两个关键点:Class<?>和...(可变参数)。
1. Class<?>:表示一个Class对象,且这个Class对象可以代表任何类型(通配符?表示未知类型)。
例如:String.class,Integer.class,int.class等。
2. ...:这是Java的可变参数(varargs)语法。它允许方法接受零个或多个指定类型的参数。
在方法内部,可变参数被当作数组处理。
所以,Class<?>... paramTypes表示:这个方法接受零个或多个Class对象作为参数,这些Class对象代表构造方法的参数类型。
这里有两个关键点:Class<?>和...(可变参数)。
1. Class<?>:表示一个Class对象,且这个Class对象可以代表任何类型(通配符?表示未知类型)。
例如:String.class,Integer.class,int.class等。
2. ...:这是Java的可变参数(varargs)语法。它允许方法接受零个或多个指定类型的参数。
在方法内部,可变参数被当作数组处理。
所以,Class<?>... paramTypes表示:这个方法接受零个或多个Class对象作为参数,这些Class对象代表构造方法的参数类型。
举个例子:
- 获取无参构造器:clazz.getConstructor()
- 获取接收一个String类型参数的构造器:clazz.getConstructor(String.class)
- 获取接收一个String和一个int类型参数的构造器:clazz.getConstructor(String.class, int.class)
在方法内部,paramTypes实际上是一个Class<?>[]数组。
- 获取无参构造器:clazz.getConstructor()
- 获取接收一个String类型参数的构造器:clazz.getConstructor(String.class)
- 获取接收一个String和一个int类型参数的构造器:clazz.getConstructor(String.class, int.class)
在方法内部,paramTypes实际上是一个Class<?>[]数组。
java.lang包
Class类
常用方法
加载类
class Person{
public String name;
public int age ;
public Person(String name, int age){
this.name = name;
this.age = age;
}
}
public String name;
public int age ;
public Person(String name, int age){
this.name = name;
this.age = age;
}
}
// 方式1:类名.class,已知类名使用
Class<?> clazz = Person.class;最安全
// 方式2:对象.getClass(),已有对象实例使用
Person pe = new Person("Bob",25);//自定义类对象
Class<?> clazz1 = pe.getClass();;
// 方式3:Class.forName("全限定类名(包括包路径)"),动态加载类使用
Class<?> clazz2 = Class.forName("Person");//自定义类类名
Class<?> clazz = Person.class;最安全
// 方式2:对象.getClass(),已有对象实例使用
Person pe = new Person("Bob",25);//自定义类对象
Class<?> clazz1 = pe.getClass();;
// 方式3:Class.forName("全限定类名(包括包路径)"),动态加载类使用
Class<?> clazz2 = Class.forName("Person");//自定义类类名
在同一个类加载器下,每个类有且只有一个 Class 对象(单例模式)
String.class时,记住它本质是JVM颁发的类身份证,而JAR文件只是存放这些身份证复印件的档案袋。反射机制正是通过这张身份证动态操控类的能力,这才是游戏MOD实现热插拔的魔法所在!
初学者推荐全泛型问号
Constructor
获取公共构造器
Constructor<?> cons = clazz.getConstructor(Class<?>... paramTypes);
(String.class , int.class);//通过类型精确匹配构造方法创建构造器
(String.class , int.class);//通过类型精确匹配构造方法创建构造器
获取任意构造器(含私有)
Constructor<?> cons = clazz.getDeclaredConstructor(Class<?>... paramTypes);
(int.class);
(int.class);
Field
获取公共字段
Field field = clazz.getField(String name);
("publicField");
("publicField");
获取任意字段(含私有)
Field field = clazz.getDeclaredField(String name);
("privateField");
("privateField");
Method
获取公共方法
Method method = clazz.getMethod(String name, Class<?>... paramTypes);
("setName", String.class);//字符串是方法名,String.class是方法参数列表的参数类型(无参使用null)
("setName", String.class);//字符串是方法名,String.class是方法参数列表的参数类型(无参使用null)
获取任意方法(含私有)
Method method = clazz.getDeclaredMethod(String name, Class<?>... paramTypes);
("internal");
("internal");
boolean
判断是否为数组类
boolean b = clazz.isArray();
Field[]
获取所有字段(含私有)
Field[] f = clazz.getDeclaredFields();
java.lang.reflect包
Constructor类(构造器)
调用方式:通过Class对象获取Constructor实例后调用
创建对象实例
Object obj = cons.newInstance(Object... initargs);
("Bob", 25);
("Bob", 25);
泛型问号使用Object
指定泛型,要使用指定泛型的类
指定泛型,要使用指定泛型的类
参数是指定类的构造方法所需的参数
突破访问限制
void = cons.setAccessible(boolean flag);
(true);
(true);
获取参数类型
Class<?>[] claz = cons.getParameterTypes();
获取构造器名
String str = cons.getName();
Field类(字段)
调用方式:通过Class对象获取Field实例后调用
设置字段值
void = field.set(Object obj, Object value);
(user, "Alice");
(user, "Alice");
获取字段值
Object obj = field.get(Object obj);
(user);
(user);
突破访问限制
void = field.setAccessible(boolean flag);
(true);
(true);
获取字段类型
Class<?> cla = field.getType();
获取字段名称
String str = field.getName();
获取修饰符编码
int i = field.getModifiers();
Method类(方法)
调用方式:通过Class对象获取Method实例后调用
调用方法
Object obj = method.invoke(Object obj, Object... args);
(user, "Hello");
(user, "Hello");
参数为类的实例对象,传递的参数(无参时使用null)
原理:使用类对象来调用方法,传入所需参数
突破访问限制
void = method.setAccessible(boolean flag);
(true);
(true);
获取返回值类型
Class<?> cla = method.getReturnType();
获取参数类型数组
Class<?>[] claz = method.getParameterTypes();
获取方法名称
String str = method.getName();
Array类(操作数组类型)
调用方式:直接通过类名调用静态方法
创建数组
Object obj = Array.newInstance(Class<?> componentType, int length);
(int.class, 5);
(int.class, 5);
获取数组元素
Object obj = Array.get(Object array, int index);
(arr, 0);
(arr, 0);
设置数组元素
void = Array.set(Object array, int index, Object value);
(arr, 0, 100);
(arr, 0, 100);
获取数组长度
int i = Array.getLength(Object array);
(arr);
(arr);
Modifier类(解析修饰符信息)
调用方式:直接通过类名调用静态方法(参数来自Field/Method/Constructor.getModifiers()
是否public
boolean b = isPublic(int mod);
是否private
boolean b = isPrivate(int mod);
是否static
boolean b = isStatic(int mod);
是否final
boolean b = isFinal(int mod);
转可读字符串
String str = toString(int mod);
Java WEB
Web概述
XML
概述
可扩展置标(标记)语言
用于传输和存储数据,标签严格区分大小写,
只有一个根元素,空格不会自动过滤,自定义和扩展性高
只有一个根元素,空格不会自动过滤,自定义和扩展性高
XML语法
文档声明
<?xml version="1.1" encoding="UTF-8" standalone="yes|no"?>
version(xml版本号)
encoding(文档使用编码集)
standalone(是否独立,yes为独立与外部文档无关,反之与外部文档嵌套)
元素定义
<element>
<元素名></元素名>
</element>
<元素名></元素名>
</element>
树状结构
有且只有一个顶层元素(文档元素,根元素)
属性定义
<元素名 属性=“属性值”></元素名>
对元素的进一步描述和说明
注释
<!-- 注释内容-->
约束
DTD约束
test.dtd文件
约束xml文件的编写规则
在xml引入dtd规则
文件
本地
<!DOCTYPE 根元素名称 SYSTEM "dtd文件的位置">
网络
<!DOCTYPE 根元素名称 PUBLIC "dtd名称" "dtd文件的URI">
内嵌
<!DOCTYPE 根元素名 [
定义语句
……
]>
定义语句
……
]>
DTD语法
定义元素
<!ELEMENT 元素名 元素内容>
元素内容
(对元素包含内容的声明)·
(对元素包含内容的声明)·
(内容模型)
(#PCDATA )
元素中嵌套的内容是普通文本字符串
<ele>Hello World!</ele>
<ele>Hello World!</ele>
(子元素,...)
元素包含其他元素(one,two)
<ele>
<one></one>
<two></two>
</ele>
<ele>
<one></one>
<two></two>
</ele>
(混合内容)
(#PCDATA | 子元素)*
元素既可以包含子元素,也可以包含字符串
*表示可以出现0次或多次。
<ele>
<子元素></子元素>
Hello World!
</ele>
*表示可以出现0次或多次。
<ele>
<子元素></子元素>
Hello World!
</ele>
关键字
EMPTY
表示元素不包含任何东西(子元素,内容)
<!ELEMENT br EMPTY>
<!ELEMENT br EMPTY>
ANY
表示元素可以包含任何字符数据和子元素
<!ELEMENT 元素名 ANY>
<!ELEMENT 元素名 ANY>
元素内容的符号
内容模型符号写在括号外,关键字直接添加
内容模型的符号()本质也是元素内容的符号()
内容模型的符号()本质也是元素内容的符号()
?
对象可以出现0次或1次
*
对象可以出现0次或多次
+
对象可以出现1次或多次
|
在列出的对象中选择一个
,
对象必须按照指定的顺序出现
()
定义一个“内容单元”或“内容模型组”
定义属性
<!ATTLIST 元素名
属性名1 属性类型 设置说明
属性名2 属性类型 设置说明
>
属性名1 属性类型 设置说明
属性名2 属性类型 设置说明
>
设置说明
#REQUIRED
XML中元素必须定义该属性
#IMPLIED
XML中元素可以定义也可以不定义该属性
#FIXED
一个固定的属性默认值(在DTD中提供)
如果XML中元素没有定义该属性,则值被自动设置为默认值
如果XML中元素没有定义该属性,则值被自动设置为默认值
默认值
例如:“你好”
与FIXED不同的是如果在XML中设置了新值
新值会覆盖DTD中默认的旧值
新值会覆盖DTD中默认的旧值
属性类型
CDATA
字符数据(与PCDATA相同),属性值如果出现特殊字符需要转义,&=&,<=<
<元素名 属性=“<属性值”></元素名>
<元素名 属性=“<属性值”></元素名>
Enumerated
枚举类型(属性值只能从一个列表中选择)
<!ATTLIST 肉 品种(鸡|牛|猪|鱼) “鸡”>
<肉 品种=“鸡”></肉>
<!ATTLIST 肉 品种(鸡|牛|猪|鱼) “鸡”>
<肉 品种=“鸡”></肉>
ID
唯一标识元素,设置说明必须是#REQUIRED或#IMPLIED
<!ATTLIST 联系人
编号 ID #REQUIRED
>
<!ATTLIST 联系人
编号 ID #REQUIRED
>
IDREF和IDREFS
将元素关联起来
一对一
<!ATTLIST 联系人
编号 ID #REQUIRED
上司 IDREF #IMPLIED
>
<!ATTLIST 联系人
编号 ID #REQUIRED
上司 IDREF #IMPLIED
>
在联系人标签中,通过上司属性将属性值设置为某ID值,将当前标签与某ID的联系人标签联系起来
一对多
引用的多个ID的属性值使用空格分隔
元素直接拥有属性
通过元素名直接关联
Schema约束
test.xsd文件
约束xml文件的编写规则
在xml引入xsd规则
子主题
XML Schema 是一个强类型系统。它要求你必须明确声明每个元素的内容模型(Content Model),而属性是这个内容模型的一部分
Schema语法
元素与属性
定义元素
<xs:element name="标签名" type="类型"/>
name(标签名称)
<ele></ele>
type="xs:
string"
字符串
decimal"
小数
integer"
整数
boolean"
布尔
date"
日期
time"
时间
定义属性
<xs:attribute name="属性名" type="类型"/>
元素具有类型,类型定义了属性(通过类型间接关联)
不能直接在元素内定义属性,
必须通过选择complexTyep和simpleType来更改标签的模型(详见下)
必须通过选择complexTyep和simpleType来更改标签的模型(详见下)
类型
简单类型
xs:simpleType
只包含字符数据的元素,不能拥有任何子元素或属性的数据类型
<xs:element name="age">
<xs:simpleType>
<xs:restriction base="xs:integer">
<xs:minInclusive value="18"/>
<xs:maxInclusive value="50"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
这里面只定义了对当前元素的限制
<xs:simpleType>
<xs:restriction base="xs:integer">
<xs:minInclusive value="18"/>
<xs:maxInclusive value="50"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
这里面只定义了对当前元素的限制
允许的子标签
1.<xs:restriction> (限制)
2.<xs:list> (列表)
3.<xs:union> (联合)
2.<xs:list> (列表)
3.<xs:union> (联合)
不允许使用extension(扩展)
复杂类型
xs:complexType
可以包含子元素和/或属性的元素类型
空元素(只包含属性)
<whats whats属性=“” />
<whats whats属性=“” />
<xs:element name="whats">
<xs:complexType>
<xs:attribute name="whats属性" type="positionInteger"/>
</xs:complexType>
</xs:element>
<xs:complexType>
<xs:attribute name="whats属性" type="positionInteger"/>
</xs:complexType>
</xs:element>
仅包含内容的元素
<size color="FFF">35</size>
<size color="FFF">35</size>
<xs:element name="元素名">
<xs:complexType>
<xs:simpleContent>
<xs:extension>
<xs:attribute name="属性名" type="xs:string"/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
</xs:element>
<xs:complexType>
<xs:simpleContent>
<xs:extension>
<xs:attribute name="属性名" type="xs:string"/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
</xs:element>
含子元素
<name>
<lastName/>
<firstName/>
</name>
<name>
<lastName/>
<firstName/>
</name>
<xs:element name="name">
<xs:complexType>
<xs:sequence>
<xs:element name="lastName" type="xs:string">
<xs:element name="firstName" type="xs:string">
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:complexType>
<xs:sequence>
<xs:element name="lastName" type="xs:string">
<xs:element name="firstName" type="xs:string">
</xs:sequence>
</xs:complexType>
</xs:element>
指定了俩个按顺序出现的子元素
包含子元素和内容的元素
<letter>
Dear Mr.<name>Liang</name>
</letter>
<letter>
Dear Mr.<name>Liang</name>
</letter>
<xs:element name="letter">
<xs:complexType>
<xs:complexContent>
<xs:element name="name" type="xs:string">
</xs:complexContent>
</xs:complexType>
</xs:element>
<xs:complexType>
<xs:complexContent>
<xs:element name="name" type="xs:string">
</xs:complexContent>
</xs:complexType>
</xs:element>
内容
xs:simpleContent(简单内容)
当一个复杂类型需要包含纯文本内容并且拥有属性,
但不能包含任何子元素时,就在 complexType内部使用 simpleContent
但不能包含任何子元素时,就在 complexType内部使用 simpleContent
xs:complexCotent(复杂内容)
当一个复杂类型需要包含子元素时,就在 complexType内部使用 complexContent。
它通常用于通过“扩展”或“限制”来修改一个已有的复杂类型
它通常用于通过“扩展”或“限制”来修改一个已有的复杂类型
属性
name
给这个自定义的复杂类型起一个唯一的名称,以便在 Schema 的其他地方(或其他 Schema 文件中)引用和复用这个类型定义
通过type属性来引用它。<xs:element name="employee" type="personType"/>
mixed
false
默认值,默认情况下一个 complexType元素只能包含纯子元素,不能在其子元素之间穿插文本内容。
true
允许在复杂类型元素的子元素之间穿插文本内容。这被称为“混合内容”
构件
类型派生机制
(从现有类型创建新的简单或复杂类型)
(从现有类型创建新的简单或复杂类型)
xs:restriction(限制)
对值的限定
xs:minInclusive(最小包含)
xs:maxInclusive(最大包含)
xs:maxInclusive(最大包含)
<xs:element name="age">
<xs:simpleType>
<xs:restriction base="xs:integer">
<xs:minInclusive value="18"/>
<xs:maxInclusive value="50"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
<xs:simpleType>
<xs:restriction base="xs:integer">
<xs:minInclusive value="18"/>
<xs:maxInclusive value="50"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
age元素的取值只能在18到50之间
不包含
xs:minExclusive(最小不包含)
xs:maxExclusive(最大不包含)
xs:maxExclusive(最大不包含)
对选项限定
xs:enumeration(枚举约束)
<xs:element name="good">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:enumeration value="apple"/>
<xs:enumeration value="banana"/>
<xs:enumeration value="tomato"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:enumeration value="apple"/>
<xs:enumeration value="banana"/>
<xs:enumeration value="tomato"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
good元素只接受apple,banana,tomato其中之一
xs:extension(扩展)
添加属性
<xs:extension>
<xs:attribute name="属性名" type="xs:string"/>
</xs:extension>
<xs:attribute name="属性名" type="xs:string"/>
</xs:extension>
模型组器
(在复杂类型中定义子元素之间的出现关系和顺序)
(在复杂类型中定义子元素之间的出现关系和顺序)
xs:sequence(顺序)
是complexType的直接子元素,
通常包含要求指定顺序出现的子元素
通常包含要求指定顺序出现的子元素
<xs:element name="name">
<xs:complexType>
<xs:sequence>
<xs:element name="lastName" type="xs:string">
<xs:element name="firstName" type="xs:string">
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:complexType>
<xs:sequence>
<xs:element name="lastName" type="xs:string">
<xs:element name="firstName" type="xs:string">
</xs:sequence>
</xs:complexType>
</xs:element>
这个规则要求了在name标签的内容中,
姓必须在名前出现
姓必须在名前出现
xs:choice(选择)
在一组子元素中,只能出现一个
xs:all(全部)
所有声明的子元素都必须出现,但顺序可以任意
每个子元素的 maxOccurs只能是 1
每个子元素的 maxOccurs只能是 1
程序开发体系架构
C/S
客户端服务器架构(Client/Server)
客户端程序——数据库服务器
(承担所有逻 (提供数据交互)
辑运算和界面显示)
(承担所有逻 (提供数据交互)
辑运算和界面显示)
1.若有多人使用,安装的工作量巨大
2.若对程序进行修改,必须对客户端程序进行修改,不利于维护和升级
3.数据库服务器支持的并发数量有限,不能同时连接庞大的客户
2.若对程序进行修改,必须对客户端程序进行修改,不利于维护和升级
3.数据库服务器支持的并发数量有限,不能同时连接庞大的客户
B/S
浏览器服务器架构(Browser/Server)
浏览器——Web服务器——数据库服务器
(只负责 (承担所有 (提供数据交互)
显示结构) 的逻辑和计算)
(只负责 (承担所有 (提供数据交互)
显示结构) 的逻辑和计算)
基本上全面取代了C/S架构
Web服务器
Tomcat
运行Servlet和JSP的容器(引擎),不仅具备WEB服务器的基本功能,还提供了数据库连接池等许多通用组件功能
URI/URL
URI(统一资源标识符)
目的:唯一地标识一个资源。核心是“身份”。
它回答的是:“这个资源叫什么?它的唯一名字是什么?”
它回答的是:“这个资源叫什么?它的唯一名字是什么?”
URL(统一资源定位符)
目的:提供获取资源的具体方法和位置。核心是“地址”和“访问方式”。
它回答的是:“这个资源在哪里?以及我如何能拿到它?”
它回答的是:“这个资源在哪里?以及我如何能拿到它?”
必须包含访问协议(如 `http://`, `ftp://`, `file://`)
所有的 URL 都是 URI,但并非所有的 URI 都是 URL
URL可以获取下载,URI不能
URL可以获取下载,URI不能
Web基础与协议
HTTP协议
HTTP版本
HTTP1.0
一次连接一次请求一次响应
(建立TCP连接-发送HTTP请求-回送HTTP响应-关闭TCP连接)
(建立TCP连接-发送HTTP请求-回送HTTP响应-关闭TCP连接)
HTTP1.1
一次连接多次请求多次响应
(建立TCP连接-n次请求-n次响应-关闭TCP连接)
(建立TCP连接-n次请求-n次响应-关闭TCP连接)
HTTP消息
HTTP请求消息
(浏览器向服务器发送请求数据)
(浏览器向服务器发送请求数据)
HTTP请求行
位于请求消息的第一行,包括请求方式,资源路径,HTTP版本
GET /index.html HTTP/1.1(注意空格)
GET /index.html HTTP/1.1(注意空格)
请求方式(8种)
GET(获取)
请求获取请求行的URO所标识的资源
POST(发送)
向指定资源提交数据,请求服务器进行处理
HEAD
请求获取URI所标识资源的响应消息头
PUT
将网页放置到指定URL位置(上传/移动)
DELETE
请求服务器删除URI所标识的资源
TRACE
请求服务器回送收到的请求信息,主要用于测试和诊断
CONNECT
保留以供将来使用
OPTIONS
请求查询服务器性能,或者查询与资源相关的选项和需求
HTTP请求头
多个头字段组成
多个头字段组成
位于请求行之后的若干行都是请求头,包括头字段名: 值(多个值时使用逗号隔开),头字段不区分大小写(一般首字母大写)
(用冒号:和空格间隔)
(用冒号:和空格间隔)
头字段(键值对)
头字段名: 头字段值
常用头字段
Accept
客户端能够接收的数据类型(MIME)(解压后)
Accept: text/html(客户端可以接收HTML文本)
Accept: image/gif(客户端可以接收GIF图像格式)
Accept: image/*(客户端可以接收所有image格式类型)
Accept: */*(客户端可以接收所有格式内容)
Accept-Encoding
客户端能够解码的数据编码格式
Accept-Encoding: gzip,compress
gzip,compress是俩种常见的数据编码格式
Host
指定资源所在的主机名和端口号
If-Modified-Since
一个请求条件,只有被请求内容的修改时间比该请求头指定的时间晚,服务器才会返回,否则返回304
Referer
直接在浏览器中输入URL发出请求
不含Referer请求头
在一个网页中点击超链接发出的请求
浏览器发送的信息包含Referer请求头,
1.可以追踪访问者是如何进入的
2.可以防盗链,盗链指一个网页的图片是通过链接到其他网站的图片来加重那个网站的服务器负担
1.可以追踪访问者是如何进入的
2.可以防盗链,盗链指一个网页的图片是通过链接到其他网站的图片来加重那个网站的服务器负担
User-Agent
包含了客户端的各种信息,包括操作系统及版本,浏览器及版本,浏览器渲染引擎/语言等
HTTP响应信息
(服务器接收请求后将处理后的
数据发送给客户端)
(服务器接收请求后将处理后的
数据发送给客户端)
HTTP响应状态行
位于响应消息第一行,包括HTTP版本,一个表示成功或失败的状态码,描述状态码的文本
状态码
1xx(信息性状态码)
2xx(成功状态码)
3xx(重定向状态码)
4xx(客户端错误状态码)
5xx(服务器错误状态码)
常见状态码
200
OK,请求成功
302
Found,临时重定向,请求的资源被临时移动到另一个位置
304
Not Modified,未修改,资源没有修改过,可以直接使用已缓存的本地资源
这是一个非常重要的性能优化状态码
这是一个非常重要的性能优化状态码
404
Not Found,未找到,服务器无法找到请求资源,这通常是客户端请求了一个未存在的链接(过期失效等等)
500
Internal Server Error,服务器内部错误
HTTP响应头
多个头字段组成
多个头字段组成
位于响应状态行之后的若干行,用于传递附加信息,包括头字段: 字段值
包括服务器程序名,被请求资源需要的认证方式,客户端请求资源的最后修改时间,重定向地址等
包括服务器程序名,被请求资源需要的认证方式,客户端请求资源的最后修改时间,重定向地址等
头字段(键值对)
头字段名: 头字段值
常用响应头字段
Location
用于重定向,告诉客户端应该跳转到哪个新URL(常于3xx或201Created状态码一起使用)
Server
描述处理请求的服务器所使用的软件信息(哪种Web服务器)
Refresh
让浏览器在指定时间(秒)后跳转到另一页面或刷新当前页面
Content-Disposition
告诉浏览器如何处理返回的内容(响应体)
inline(内联)
默认,图片html等浏览器可以直接渲染的内容
attachment(附件)
下载到本地
搭配filename建议保存到本地时的默认名称
请求处理核心
Servlet技术
Servlet概述
运行在服务器端的小程序,用于处理动态请求并生成响应
客户端→Web服务器→Servlet容器(Tomcat)→Servlet→Web服务器→客户端
客户端→Web服务器→Servlet容器(Tomcat)→Servlet→Web服务器→客户端
Servlet特点
基于java
继承了跨平台性,安全性等
高效
采用多线程处理请求,性能优秀
可移植性
基于java和标准API,编写好的Servlet可部署到任何支持Servlet的Wen服务器
功能强大
可轻松完成复杂的任务
生命周期由容器管理
创建,初始化,调用,销毁由容器(Tomcat)负责,开发者只需处理逻辑
Servlet生命周期
初始化(Init)
容器第一次收到指向该Servlet的请求时,创建Servlet实例并调用实例的Init()方法
处理请求(Service)
每次客户端请求时,容器都会创建一个新线程,并调用Servlet实例的service()方法,
service()会根据请求的类型调用相应的方法(GET,POST→doGet(),doPost())
service()会根据请求的类型调用相应的方法(GET,POST→doGet(),doPost())
销毁(Destroy)
当应用关闭、重新部署、容器需要移除时,容器会调用destroy()释放占用资源,关闭连接等
接口
Servlet接口(核心)
根接口,定义了Servlet生命周期的相关方法
(通常不直接实现这个接口)
(通常不直接实现这个接口)
void Init()
启动
void service()
请求
void destroy
销毁
ServletConfig getServiceConfig()
获取当前Servlet的ServletConfig对象
String getServletInfo()
获取一些Servlet的信息,作者、版本等,但通常返回空字符串或null
我们通常继承已经实现了Servlet方法的抽象类HttpServlet,并重写doGet()、doPost()等方法
ServletConfig接口
此接口的对象代表一个Servlet的配置信息,每个Servlet都只有一个ServletConfig对象,在容器调用Init()时由容器创建并传入
主要用于获取Servlet的初始化参数和Servlet上下文
主要用于获取Servlet的初始化参数和Servlet上下文
常用方法
String getServletName()
获取当前Servlet实例的名称(在web.xml中的<servlet-name中配置>)
ServletContext getServletContext
最重要的方法之一,用于获取Web应用的ServletContext对象
参数
String getInitParamater(String name)
获取指定名称的初始化参数
参数位置:web.xml中该Servlet的<servlet>标签下的<init-param>中
Enumeration<String> getInitParameterName()
返回当前Servlet的所有初始化参数名
ServletContext接口
一个We应用在容器中有且只有一个ServletContext对象,代表了应用本身
所有Servlet共享该对象
所有Servlet共享该对象
核心方法
参数
String getInitParameter(String name)
获取指定名称的全局初始化参数
参数位置:web.xml的根标签<web-app>标签下,通过<context-param>配置
<web-app>
<!-- 全局初始化参数,所有Servlet都可访问 -->
<context-param>
<param-name>jdbcURL</param-name>
<param-value>jdbc:mysql://localhost:3306/mydb</param-value>
</context-param>
<context-param>
<param-name>appName</param-name>
<param-value>My Awesome App</param-value>
</context-param>
...
</web-app>
Enumeration<String> getInitParameterName()
返回所有全局初始化参数名
属性
设置属性对象
void setAttribute(String name , Object onkect)
将一个对象以键值对的形式存储到应用范围(Application Scope)中,供所有用户和所有 Servlet 共享
获取属性对象
Object getAttribute(String name)
移除属性
void removeAttribute(String name)
路径
转换
String getRealPath(String path)
将Web应用内部的相对路径转换为服务器绝对路径,在读取资源文件时很有用
获取子目录路径
Set getResoucePaths(String path)
参数为要获取子目录的父目录路径,必须以/开头
返回映射到指定路径的URL对象
URL getResource(String path)
返回映射到指定路径的输入流对象
InputStream getResourceAsStream(String path)
请求调度器
获取对象
// 使用ServletContext获取(需要先获取ServletContext),并不常用,常用HttpServletRequest获取请求调度器
RequestDispatcher rd2 = getServletContext().getRequestDispatcher("/otherServlet");
99% 的情况下,应该使用 HttpServletRequest.getRequestDispatcher(),原因如下:
1. 更直观:它明确表示这个转发是针对当前请求的。
2. 路径灵活性:既可以接受绝对路径(以 `/` 开头),也可以接受相对路径。
3. 更安全:它会考虑请求的路径信息,比如如果请求已经被转发过,它仍然能正确工作。
常用方法
转发
void forward(request , response)
完全移交到rd2所指定路径上的(其他Servlet,jsp…)
包含
void include(request , response)
临时借用,rd2处理完返回给发起包含方法的Servlet
Http消息
ServletResponse接口
HttpServletResponse接口
功能:用于封装Http响应信息
方法
Http响应状态行
发送状态响应码
设置状态码,生成状态行
void setStatus(int sc)
使用预定义常量
HttpServletResponse.SC_OK(200)
HttpServletResponse.SC_OK(200)
SC_CREATED(201)
SC_ACCEPTED(202)
SC_NO_CONTENT(204)
SC_NO_CONTENT(204)
SC_BAD_REQUEST(400)
SC_UNAUTHORIZED(401)
SC_UNAUTHORIZED(401)
SC_NOT_FOUND(404)
SC_INTERNAL_SERVER_ERROR(500)
发送错误状态码
void sendError(int sc)
发送错误状态码,同时显示错误html页面
void sendError(int sc , String message)
需要显示的错误提示文本
需要显示的错误提示文本
自动发送302
void sendRedirect(String location)
这是一个便捷方法,专门用于发送一个 302 (Found)重定向状态码,
并同时设置 Location响应头,告诉浏览器自动跳转到另一个URL。
并同时设置 Location响应头,告诉浏览器自动跳转到另一个URL。
Http响应头
发送响应头
设置响应头字段
字符串值
void setHeader(String name , String value)
设置同名响应头字段会覆盖
void addHeader(String name , String value)
可以增加同名响应头字段
整数值
void setIntHeader(String name , int value)
设置同名响应头字段会覆盖
void addIntHeader(String name , int value)
可以增加同名响应头字段
设置响应消息的实体内容大小
void setContentLength(int len)
单位为字节,在HTTP中,这个方法设置的是Content-Length头字段的值
设置Servlet输出内容的MIME类型
void setContentType(String type)
在HTTP中,这个方法设置的是Content-Typr头字段的值
如果输出的响应消息是文本,还可以设置字符编码text/html;charset=UTF-8
如果输出的响应消息是文本,还可以设置字符编码text/html;charset=UTF-8
设置本地化消息
void setLocale(Locale loc)
在HTTP中,就是设置Content-Language响应头字段和Content-Type头字段的字符集编码部分
如果没有设置Content-Type,则setLoacate不生效不会出现在响应消息中
如果调用setCharacterEncoding()或setContentType()指定响应内容的字符集编码,setLocate()不生效
如果没有设置Content-Type,则setLoacate不生效不会出现在响应消息中
如果调用setCharacterEncoding()或setContentType()指定响应内容的字符集编码,setLocate()不生效
设置输出内容使用的字符编码
void setCharacterEncoding(String charset)
在HTTP中,就是设置Content-Type头字段的字符集编码部分
如果没有设置Content-Type,则setCharacterEncoding不生效不会出现在响应消息中
该方法的优先级高于setContentType()和setLoacte()会覆盖俩个方法设置的编码
如果没有设置Content-Type,则setCharacterEncoding不生效不会出现在响应消息中
该方法的优先级高于setContentType()和setLoacte()会覆盖俩个方法设置的编码
发送响应消息体
获取字节输出流对象
ServletOutputStream getOutputStream()
获取字符输出流对象
PrintWriter getWriter()
使用
实现请求重定向
利用setRedirect(String Location)
解决乱码
setCharacterEncoding("utf-8")
setHeader("Content-Type" , "text/html;charset=utf-8")
setHeader("Content-Type" , "text/html;charset=utf-8")
更推荐的简洁方法
setContentType("text/html;charset=utf-8")
消息对象
一般来说,我们不需要手动创建ServletRequest和ServletResponse对象。
它们是由Servlet容器(如Tomcat、Jetty等)在接收到HTTP请求时自动创建的,并在整个请求处理过程中传递。
它们是由Servlet容器(如Tomcat、Jetty等)在接收到HTTP请求时自动创建的,并在整个请求处理过程中传递。
ServletRequest接口
HttpServletRequest接口
功能:用于封装HTTP请求消息
方法
Http请求行
获取请求行相关
(看上面HTTP协议)
(看上面HTTP协议)
获取请求方式
String getMethod();
获取资源名称
(URL主机和端口之后,参数之前的部分)
(URL主机和端口之后,参数之前的部分)
String getRequestURI();
获取参数
(资源路径后面?以后的所有内容)
(资源路径后面?以后的所有内容)
String getQueryString();
获取协议名和版本
String getProtocol();
HTTP/1.0…
获取请求的协议名
String getScheme();
获取请求的URL中属于Web应用的路径
String getContextPath();
返回的是应用的部署路径,由服务器配置决定,
对于所有客户端都是*固定不变*的,以/开头
对于所有客户端都是*固定不变*的,以/开头
获取Servlet名称和路径
(服务器端决定启动哪个servlet的关键)
(服务器端决定启动哪个servlet的关键)
String getServletPath();
获取请求行中的servlet名称和地址来启动对应servlet
请求行会包含URL,在前端页面设置的URL,点击对应内容就会发送…
请求行会包含URL,在前端页面设置的URL,点击对应内容就会发送…
远程(客户端)的
IP地址
String getRemoteAddr();
主机名
String getRemoteHost();
端口号
int getRemotePost();
接收客户请求的
本地(服务器)IP地址
String getLocalAddr();
本地(服务器)主机名
String getLocalName();
本地(服务器)端口号
int getLocalPost();
客户要访问
服务器主机名
String getServerName();
服务器端口号
int getServerPort();
StringBuffrer getRequestURL();
获取客户端请求的完整URL(协议,服务器名,端口号,资源路径),
不包括查询参数
不包括查询参数
Http请求头
多个头字段组成
多个头字段组成
头字段(键值对)
头字段名: 头字段值
指定名称获取
获取指定头字段的值
单个
String getHeader(String name);
如果没有,返回null
如果多个,返回第一个指定头字段的值
如果多个,返回第一个指定头字段的值
多个
Enumeration getHeader(String name);
指定名称的头字段的值组成的集合
获取所有请求头字段(键值对)
Enumeration getHeaderNames() ;
转换获取
将指定头字段的值转为int类型并返回
int getIntHeader(String name);
将指定头字段的值转为long类型并返回
long getDateHeader(String name);
将头字段的值按MT时间格式转换为一个长整数(代表时间/日期)
这个长整数是从1970年1月1日0点0分0秒算起的以毫秒为单位的时间值
这个长整数是从1970年1月1日0点0分0秒算起的以毫秒为单位的时间值
获取特定头字段
获取Content-Type头字段的值
String getContentType();
获取Content-Length头字段的值
String getContentLength();
获取字符集编码
(通常从Content-Type提取)
(通常从Content-Type提取)
String getCharacterEncoding();
如果有Content-Type且包含编码则直接提取,如果不包含可能返回null或默认编码
如果没有Content-Type头字段,可能返回null
如果没有Content-Type头字段,可能返回null
请求转发
获取请求调度器
RequestDispatcher getRequestDispatcher(String Path);
获取某路径所指定资源的调度器对象
Servlet之间的跳转
转发
void forwards(ServletRequest request, ServletResponse response);
完全移交到rd2所指定路径上的(其他Servlet,jsp…)
包含
void include(ServletRequest request, ServletResponse response);
临时借用,rd2处理完返回给发起包含方法的Servlet
方法
获取请求参数
功能
获取用户提交的表单数据,例如:用户名、密码、电子邮件
方法
获取指定名称的参数值
String getParameter(String name);
指定名称的参数不存在时,返回null
指定名称的参数存在且值为空时,返回空字符串
指定名称的参数存在且有多个值时,返回第一个出现的值
指定名称的参数存在且值为空时,返回空字符串
指定名称的参数存在且有多个值时,返回第一个出现的值
获取指定名称的所有参数值
String[] getParameterValues(String name);
获取所有参数名
Enumeration getParameterNames();
将所有参数及值以键值对形式存入Map
Map getParameterMap();
通过Request对象传递数据
特点
操作属性
只有同一个请求中的数据才可以通过ServletRequest对象传递数据
方法
在ServletRequest对象中设置属性
void setAttribute(String name ,Object o);
将name与对象o关联后存储进ServletRequest对象中
如果name已经存在,则删老用新
如果o为null ,则删除指定name,等效于removeAttribute()
从ServletRequest对象中获取属性
Object getAttribute(String name);
在ServletRequest对象中删除属性
void removeAttribute(String name);
获取ServletRequest对象中所有属性
Enumeration getAttributeNames();
Servlet高级特性
状态管理
会话及会话技术
Cookie API
特点
存储在客户端,只能是字符串对象,安全性低,可长期保存在客户端,不影响服务器
有容量限制,每个月4KB,每个网站约20个
有容量限制,每个月4KB,每个网站约20个
存储不敏感,非关键数据
构造方法
Cookie c = Cookie(String name, String value)
// 创建一个名为 "username" 值为 "john_doe" 的 Cookie
Cookie userCookie = new Cookie("username", "john_doe");
// 创建一个名为 "language" 值为 "zh-CN" 的 Cookie
Cookie langCookie = new Cookie("language", "zh-CN");
Cookie userCookie = new Cookie("username", "john_doe");
// 创建一个名为 "language" 值为 "zh-CN" 的 Cookie
Cookie langCookie = new Cookie("language", "zh-CN");
Cookie创建后,名称无法再更改,值可以在创建后修改
常用方法
Cookie名称
String getName();
Cookie值
void setValue(String newValue);
String getValue();
Cookie在客户端保持有效的秒数
void setMaxAge(int expiry);
int getMaxAge();
Cookie项的有效目录路径
void setPath(String uri);
String getPath();
Cookie项的有效域
void setDomain(String pattern);
String getDomain();
Cookie采用的协议版本
void setVersion(int v);
int getVersion();
Cookie的注解部分
void setComment(String purpose);
String getComment();
Cookie项是否只能只能使用安全协议传送
void setSecure(boolean flag);
boolean getSecure()
HttpSession API
特点
存储在服务端,可以是任意Java对象,安全性较高,一般随会话结束销毁,影响服务器性能
容量限制取决于服务器内存
容量限制取决于服务器内存
存储敏感,关键数据
Session的实现依赖于Cookie发送SessionID
获取对象
使用HttpServletRequest对象调用方法获取
HttpSession getSession(boolean create)
参数为true时,HttpSession不存在时会创建新的
为false,HttpSession不存在时会返回null
为false,HttpSession不存在时会返回null
常用方法
视图呈现
JSP技术
概述
JSP文件内容的构成
主要构成是 Web代码 + <% Java代码 %>
JSP脚本元素
嵌套在<% %>内的Java代码
类
变量
成员变量
类变量(静态变量)
声明为static的静态变量
口语化称为全局变量
属于类本身,所有类的实例共享
在类加载时初始化,且只初始化一次
可以通过类名访问,也可以使用对象访问
实例变量(类的属性)
非静态变量(可进行封装public,protected,default,private)
属于类的实例(对象)
在创建对象时初始化,每创建一个新对象初始化一次
必须通过对象实例访问
局部变量
(局部变量没有访问控制权限,无法被其他类访问,无法进行封装)
属于成员方法,只能在方法内使用
成员方法
和类方法(静态方法)
实例方法
类型
JSP Scriptlets
可以定义属性,也可以输出内容
<%
定义属性:
int a = 1;
String name = "YLiang"
输出内容:
out.println(name + a );
%>
定义属性:
int a = 1;
String name = "YLiang"
输出内容:
out.println(name + a );
%>
输出的是一个字符串,相当于
<body>
YLaing1
</body>
<body>
YLaing1
</body>
声明标识
可以定义在整个JSP页面有效的变量,方法
可以定义类的成员,包括实例(变量/方法)、类(静态变量/方法)以及静态代码块。
<%!
int a = 1;
public String toStr(){
String name = "Liang";
int b = 11086;
return name+ String.valueOf(b);
}
%>
int a = 1;
public String toStr(){
String name = "Liang";
int b = 11086;
return name+ String.valueOf(b);
}
%>
<%
调用方法:
out.println(toStr() + “<br>”);
调用变量:
out.println(a);
%>
调用方法:
out.println(toStr() + “<br>”);
调用变量:
out.println(a);
%>
HTML不认ln换行,使用<br>标签
JSP表达式
(expression)
(expression)
将表达式运算结果转换为一个字符串
<%=
a+b
%><br>
a+b
%><br>
<%!
int a = 3485;
int b = 2940;
%>
int a = 3485;
int b = 2940;
%>
JSP注释
代码片段的注释(同Java)
单行//
多行/**/
提示文档/** */
多行/**/
提示文档/** */
隐藏注释
<%-- 隐藏内容--%>
动态注释
就是将内容输出到HTML注释中
<!-- <%=new Date()%-->
JSP指令
指令格式
<%@ 指令类型 属性名="属性值" %>
指令类型
page
对页面的某些特性进行描述,例如使用的语言,文件的路径,导入的java类
include
taglib
EL和JSTL
JavaBean技术与JSP开发模型
数据持久化
JDBC
数据库连接池与DBUtils工具
现代交互实时更新
Ajax
Android Studio
配置安卓
Android studio 2024.1:https://redirector.gvt1.com/edgedl/android/studio/install/2024.1.2.12/android-studio-2024.1.2.12-windows.exe
中文包:
Gradle(镜像网国内):
腾讯: https://mirrors.cloud.tencent.com/gradle/
阿里: https://mirrors.aliyun.com/macports/distfiles/gradle/
在项目-gradle-wrapper-gradle-wrapper.properties中更改
从distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip
改成distributionUrl=https\://mirrors.cloud.tencent.com/gradle/gradle-8.7-bin.zip
放在C:\Users\L(用户名)\.gradle\wrapper\dists\gradle-8.7-bin\af3un6e4ivqgjcdo5lfa5efog目录下
中文包:
Gradle(镜像网国内):
腾讯: https://mirrors.cloud.tencent.com/gradle/
阿里: https://mirrors.aliyun.com/macports/distfiles/gradle/
在项目-gradle-wrapper-gradle-wrapper.properties中更改
从distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip
改成distributionUrl=https\://mirrors.cloud.tencent.com/gradle/gradle-8.7-bin.zip
放在C:\Users\L(用户名)\.gradle\wrapper\dists\gradle-8.7-bin\af3un6e4ivqgjcdo5lfa5efog目录下
基础安卓
基础学习
剖析
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<application
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"//声明应用图标
android:label="@string/app_name"//声明应用名称
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.MyApplication"
tools:targetApi="31">
<activity
android:name=".MainActivity" 设置运行的Activity.java文件,在测试学习中大有用处
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<application
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"//声明应用图标
android:label="@string/app_name"//声明应用名称
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.MyApplication"
tools:targetApi="31">
<activity
android:name=".MainActivity" 设置运行的Activity.java文件,在测试学习中大有用处
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
<!-- android:exported="true"表示这个Activity是否可以被外部应用程序的组件启动。>-->
<!--如果Activity包含<intent-filter>,则exported的默认值为true;如果不包含,则默认值为false-->
<!--用于指定该Activity能够响应的Intent类型。
- 在第一个Activity中,它包含了一个<intent-filter>,其中:
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
- 这个组合非常常见,它表示这个Activity是应用程序的主入口点,即当用户点击应用图标时启动的Activity。
同时,它会在系统的应用启动器中显示一个图标和标签。-->
<!--如果Activity包含<intent-filter>,则exported的默认值为true;如果不包含,则默认值为false-->
<!--用于指定该Activity能够响应的Intent类型。
- 在第一个Activity中,它包含了一个<intent-filter>,其中:
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
- 这个组合非常常见,它表示这个Activity是应用程序的主入口点,即当用户点击应用图标时启动的Activity。
同时,它会在系统的应用启动器中显示一个图标和标签。-->
请看下方Intent章节
MainActivity.java
package com.example.myapplication;
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
Bundle savedInstanceState每次打开时恢复上次样式
目录
命名
变量命名
必须小写字母开头
只能包含:a-z, 0-9, _
不能使用 XML 关键字
必须小写字母开头
只能包含:a-z, 0-9, _
不能使用 XML 关键字
资源命名
只能包含小写字母(a-z)、数字(0-9)、下划线(_)和点(.)
不能以数字开头
不能包含大写字母、连字符(-)、空格或其他特殊字符
不能与 Android 关键字冲突(如 R.java、res、drawable 等)
不能以数字开头
不能包含大写字母、连字符(-)、空格或其他特殊字符
不能与 Android 关键字冲突(如 R.java、res、drawable 等)
xmlns
xmlns:android="http://schemas.android.com/apk/res/android"
基本每个xml文件的第一个标签内都会使用,推荐记住
Android资源配置量词
单位
dpi
Density
表示每英寸物理长度上排列的物理像素点的数量
480dpi
每英寸物理长度上排列的物理像素点为480px
一般为已知,用来运算出dp或px
物理像素px
屏幕的最小显示单元
逻辑像素dp
软件编程使用的虚拟单位
为了在不同dpi的屏幕下的组件具有相似大小
在高dpi下会放大组件
px与dp的关系
在160dpi屏幕上:1 dp = 1 物理像素
1px = 1dp * (160 / 设备dpi)
在480dpi的手机上,1px = 0.666667dp
1dp = 1px * (设备dpi / 160)
在480dpi的手机上,1dp = 3px
收获
手机大小用dp,分辨率用dpi
dpi/160可以获得分辨率与大小的关系,480/160=3
安卓开发使用dp作为单位,避免使用px,从而达到在不同dpi手机上组件有相似的大小(文本大小除外)
文本使用sp作为单位
sp 的尺寸会跟随用户在系统设置中调整的“字体大小”或“显示大小”而变化。
用户设置为“小”:1 sp ≈ 0.8 dp (实际比例因子可能不同)
用户设置为“默认”(Normal):1 sp = 1 dp
用户设置为“大”:1 sp ≈ 1.2 dp
用户设置为“超大”:1 sp ≈ 1.4 dp
用户设置为“小”:1 sp ≈ 0.8 dp (实际比例因子可能不同)
用户设置为“默认”(Normal):1 sp = 1 dp
用户设置为“大”:1 sp ≈ 1.2 dp
用户设置为“超大”:1 sp ≈ 1.4 dp
前端知识
前端使用px作为单位,然后利用DPR缩放大小匹配
DPR=物理像素/逻辑像素
屏幕分辨率
ldpi
低密度屏幕分辨率,120dpi(120点/英寸)
mdpi
中分辨率,160dpi
hdpi
高分辨率,240dpi
xhdpi
超高分辨率,320dpi
xxhdpi
超超高分辨率,480dpi
xxxhdpi
超超超高分辨率,640dpi
屏幕大小
small
smallest Width < 600dp
normal
600dp ≤ sw < 720dp
large
720dp ≤ sw < 960dp
xlarge
960dp ≤ sw
屏幕方位
port
竖屏
land
横屏
API版本
values-v4
API版本为4
v7
API版本为7
v29
API版本为29
在不同API版本的手机上使用不同的values-vX下的资源
语言和地区
cn
中文
en
英文
fr
法文
fr-rCA
法文,加拿大地区
Android应用程序资源(res)
分类
drawable
其他图片资源
基本路径
res/drawable
引用
Java
R.drawable.picname
XML
<ImageView
android:src="@drawable/profile_picture"
/>
android:src="@drawable/profile_picture"
/>
layout
布局资源
基本路径
res/layout/activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
</androidx.constraintlayout.widget.ConstraintLayout>
引用
Java
在MainActivity中设置显示布局界面
setContentView(R.layout.activity_main)
ID资源
基本路径
res/layout/activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
</androidx.constraintlayout.widget.ConstraintLayout>
添加ID
@+id
android:id="@+id/tv01"
命名要求
必须小写字母开头
只能包含:a-z, 0-9, _
不能使用 XML 关键字
必须小写字母开头
只能包含:a-z, 0-9, _
不能使用 XML 关键字
引用ID
XML
@id
app:layout_constraintLeft_toLeftOf="@id/tv01"
Java
前提设置了布局
setContentView(R.layout.activity_main);
在onCreate()方法中使用,避免才onCreate()方法前使用,因为setContentView()方法运行后才能查找到
findViewById(R.id.xxx)
btn = this.findViewById(R.id.buttonid_01);
menu
菜单资源
基本路径
res/menu/menus.xml
引用
Java
一般在重写方法中使用,
作用是绑定到显示的layout
作用是绑定到显示的layout
public boolean onCreateOptionsMenu(Menu menu){
this.getMenuInflater().inflate(R.menu.menus,menu);//将 XML 中定义的菜单项(如 <item>),解析并添加到menu对象中
return true;
}
this.getMenuInflater().inflate(R.menu.menus,menu);//将 XML 中定义的菜单项(如 <item>),解析并添加到menu对象中
return true;
}
mipmap
应用图标资源
基本路径
默认目录
res/mipmap
细分目录
高分辨率设备
res/mipmap-hdpi
中分辨率设备
res/mipmap-mdpi
低分辨率设备
res/mipmap-ldpi
xhdpi,xxhdpi等
作用
根据不同dpi使用不同图标
引用
一般不在Java用
在XML文件中
(activity_main.xml)
(activity_main.xml)
<ImageView
android:src="@mipmap/picname"
/>
android:src="@mipmap/picname"
/>
values
字符串资源(strings.xml)
本质
@string/ 是 Android 资源系统的固定前缀,用于指向 res/values/ 目录下的所有字符串资源文件(不限于 strings.xml)
定义
<string name="text01">你好</string>
引用
Java文件中
字符串资源:通过R.string.资源名(例如:R.string.app_name)引用。
tv.setText(R.string.group)
视图ID资源:通过R.id.资源名(例如: TextView tv = findViewById(R.id.tv01)引用。
其他更改
tv.setText("你好!!")
在XML文件中
(activity_main.xml)
(activity_main.xml)
字符串资源:通过`@string/资源名`(例如:`@string/app_name`)引用
android:text="@string/hello"
<TextView
android:text="@string/hello"
/>
android:text="@string/hello"
/>
尺寸资源(dimens.xml)
定义
<dimen name="text_size">50sp</dimen>
引用
android:textSize="@dimen/text_size"
颜色资源(colors.xml)
定义
<color name="primary_color">#6200EE</color>
引用
android:textColor="@color/primary_color"
颜色
RGB(十六进制)
#FF0000
00对应0,FF对应255
ARGB(十六进制)_
#80FF0000
A代表透明通道,80转为十进制为128,约为255的一半,所以是50%透明度
样式资源(styles.xml)
定义
<style name="BoldText">
<item name="android:textSize">@dimen/size</item>
<item name="android:textStyle">bold|italic</item>
<item name="android:typeface">serif</item>
</style>
<item name="android:textSize">@dimen/size</item>
<item name="android:textStyle">bold|italic</item>
<item name="android:typeface">serif</item>
</style>
引用
style="@style/BoldText"
整数资源(integers.xml)
数组资源(arrays.xml)
themes
主题资源(themes.xml(night))
xml
一些系统规则
引用资源
不同引用
Java
[package.]R.type.name
XML
@[package:]type/name
type
drawable(图片资源)
id(在布局中为组件赋予的ID)
layout(布局资源)
string(字符串资源)
menu(菜单资源)
string-array(字符串数组资源)
Activity(Java)
生命周期
onCreate()
Activity首次创建
onStart()
Activity由不可见变为可见
onResume()
Activity进入前台并获焦点
onPause()
失去焦点(如弹出对话框)
onStop()
完全不可见(如新Activity覆盖)
onRestart()
从停止状态重新启动
onDestroy()
Activity被销毁前
finish()
Android平台会自动按顺序调用P,S,D
Log类
导入
import android.util.Log
TGA
建议为每个Activity分配一个TGA
private static final String TAG = "MainActivity"; // 添加日志标签
方法
调试
Log.d()
Log.d(TAG, "onStart方法被运行了");
最常用,性能好,发布时自动删除
状态
Lod.i()
Log.i("Login", "用户点击登录按钮");
性能较差
错误
Log.e()
try {
// 登录逻辑
} catch (AuthException e) {
Log.e("Login", "认证失败,原因:" + e.getMessage(), e);
}
// 登录逻辑
} catch (AuthException e) {
Log.e("Login", "认证失败,原因:" + e.getMessage(), e);
}
常见的Activity及其子类
后续介绍
常用方法
绑定组件(通过ID)
View v = findViewById(R.id.test);
有同名局部变量时,使用this.findViewById())指定使用当前类对象调用
获取绑定组件ID
int i = v.getId();
UI
XML布局
将应用程序界面写入xml文件中,在应用程序业务逻辑(java)文件中调用
setContentView(R.layout.activity_main)
setContentView(R.layout.activity_main)
View(顶级父类)
(android.view包)
(android.view包)
继承自android.view.View
XML配置属性
组件ID
android:id
视图
宽
android:layout_width
高
android:layout_height
属性值
视图的宽度或高度会刚好包裹住视图的内容
wrap_content
在设置wrap_content后可以设置最大小宽高
android:minHeight
maxWidth
android:minHeight
maxWidth
视图的宽度或高度会扩展到与父布局相同
match_parent
指定一个固定的大小,单位可以是 dp、px 等(推荐使用 dp)
100dp……
组件背景
android:background
推荐drawable资源引用
组件是否可见
android:visibility
visible
默认值,视图可见且可交互
invisible
视图不可见但仍占据布局空间(用户无法看到或交互)
gone
视图不可见且不占用布局空间(用户无法看到或交互)
组件是否相应点击事件
android:clickable
true,false
组件的内边距
android:padding
android:padding
Top
Bottom
Left
Right
Start
End
Horizontal(水平)
Vertical(垂直)
Top
Bottom
Left
Right
Start
End
Horizontal(水平)
Vertical(垂直)
简写格式遵循值复制原则,不支持简写顺时针原则
组件外边距
android:layout_margin
android:layout_margin
Top
Bottom
Left
Right
Start
End
Horizontal(水平)
Vertical(垂直)
Top
Bottom
Left
Right
Start
End
Horizontal(水平)
Vertical(垂直)
简写格式遵循值复制原则,不支持简写顺时针原则
透明度
android:alpha
<!-- 0.0(全透明) ~ 1.0(不透明) -->
对齐方式
容器中使用
android:gravity
容器组件使用
androi:layout_gravity
center(水平垂直居中)
center_horizontal(水平居中)
center_horizontal(水平居中)
布局
推荐
FrameLayout,LinearLayout
不推荐
RelativeLayout(主要通过相对居中),ConstraintLayout(主要通过约束居中)
圆角边框
使用 XML Drawable(推荐)
主题设置
android:theme
Java方法
Android 严格遵循 JavaBean 规范,通过 setter(设置器)和 getter(获取器)方法,将 XML 中预定义的属性与代码逻辑关联。
这一设计实现了“静态 XML 配置”与“动态代码控制”的互补。
这一设计实现了“静态 XML 配置”与“动态代码控制”的互补。
几乎所有通过 XML 定义的预定义属性(如 android:visibility)都对应 Java/Kotlin 中的 setXxx()和getXxx() 方法,这是遵循 JavaBean 规范的设计原则
子类缩略图
View
TextView
Button
CompoundButton
CheckBox
RadioButton
Switch
ToggleButtom
ImageView
EditText
ViewGroup
LinearLayout
RelativeLayout
FrameLayout
ScrollView
HorizontalScrollView
ConstraintLayout
视图组件
(android.widget包)
(android.widget包)
View子类
基础组件
XML引入
<android.widget.TextView />
实际开发中通常不推荐的全限定名写法
<TextView
XML配置属性
/>
XML配置属性
/>
通常直接写组件名称
类别
测试时,可以使用View组件
TextView(文本组件)
子类
Button
所有XML配置属性都继承自TextView
XML配置属性
组件显示文本
android:text
推荐使用字符串资源引用
android:text不能包含空格
android:text不能包含空格
组件文本颜色
android:textColor
推荐颜色资源引用
组件文本大小
android:textSize
推荐单位度量资源引用
文本使用sp作为单位
sp 的尺寸会跟随用户在系统设置中调整的“字体大小”或“显示大小”而变化。
用户设置为“小”:1 sp ≈ 0.8 dp (实际比例因子可能不同)
用户设置为“默认”(Normal):1 sp = 1 dp
用户设置为“大”:1 sp ≈ 1.2 dp
用户设置为“超大”:1 sp ≈ 1.4 dp
用户设置为“小”:1 sp ≈ 0.8 dp (实际比例因子可能不同)
用户设置为“默认”(Normal):1 sp = 1 dp
用户设置为“大”:1 sp ≈ 1.2 dp
用户设置为“超大”:1 sp ≈ 1.4 dp
<dimen name="size">50sp</dimen>
android:textSize="@dimen/size"
组件文本风格
android:textStyle
bold
粗
italic
斜
bold | italic
不能有空格(预定义常量int值)
组件文本字体
android:typeface
normal, sans, serif, monospace
默认 无衬线体 衬线体 等宽字体
(拥有衬线装饰的字体)
默认 无衬线体 衬线体 等宽字体
(拥有衬线装饰的字体)
组件文本对齐方式
android:gravity
center,left,right,top
Java
设置文本
tv.setText(books[index].bookname);
接收的是String类型,非String使用Sting.valueOf();
添加文本
tv.append()
ImageView(图片组件)
要点
图片放在D:\Tool\Android\UITest\app\src\main\res\drawable下
图片要符合命名规范
只能包含小写字母(a-z)、数字(0-9)、下划线(_)和点(.)
不能以数字开头
不能包含大写字母、连字符(-)、空格或其他特殊字符
不能与 Android 关键字冲突(如 R.java、res、drawable 等)
不能以数字开头
不能包含大写字母、连字符(-)、空格或其他特殊字符
不能与 Android 关键字冲突(如 R.java、res、drawable 等)
XML配置属性
组件最大高度
android:maxHeight
组件最大宽度
android:maxWidth
图片路径
android:src
图片描述文字
android:contentDescription
屏幕阅读器,辅助视障人士
图片的缩放与对齐
android:scaleType
center
center
不缩放,不裁剪,保持比例居中
auto
centerCrop
缩放,裁剪,保持比例居中
cover
centerInside
缩放,不裁剪,保持比例居中
contain
是 fitCenter 的变体(更强调“不裁剪”)。
fit
fitCenter
缩放,不裁剪,保持比例居中
contain
fitStart/fitEnd
缩放,不裁剪,保持比例左上/右下
contain
fitXY
不裁剪,不保持比例,强制拉伸变形居中
100% 100%
matrix
可编程控制
Java
设置图片
iv.setImageResource(books[index].photo);
EditText(可编辑文本组件)
父类
TextView,最终父类View
XML配置属性
大部分继承自父类
特有属性
已过时
为了兼容和旧代码未删除
为了兼容和旧代码未删除
自动纠正拼写(已废弃,用 inputType 替代)
android:autoText
android:inputType="textAutoCorrect" <!-- 自动拼写纠正 -->
自动首字母大写(已废弃,用 inputType 替代)
android:capitalize
android:inputType="textCapWords" <!-- 每个单词首字母大写 -->
强制单行显示(已废弃,用 maxLines="1" 或 inputType 替代)
android:singleLine
android:maxLines="1" <!-- 限制为1行(推荐) -->
android:inputType="text" <!-- 默认单行输入 -->
android:inputType="text" <!-- 默认单行输入 -->
输入提示文本(如“请输入用户名”)
android:hint
推荐引用字符串资源
限制只能输入指定字符(如数字、符号)
android:digits
推荐直接硬编码 digits
限制只能输入数字
android:digits="0123456789" <!-- 只能输入数字 -->
只能输入字母
android:digits="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" <!-- 只能输入字母 -->
只能输入数字+字母
android:digits="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" <!-- 字母+数字 -->
只能输入特定符号
android:digits=".-" <!-- 只能输入 "." 和 "-" -->
不像inputType一样会改变键盘类型
最大可输入字符
android:maxLength
多行情况下包含\n
android:maxLength="10" <!-- 最多输入 10 个字符 -->
最大显示行数
android:maxLines
android:maxLines="1" <!-- 限制为1行-->
核心
定义输入类型
android:inputType
单一类型
文本类型
默认类型
text
多行文本
textMultiLine
搭配maxLines
特定内容类型
数字
number
纯数字输入(键盘显示数字键)
电话号码
phone
电话号码键盘(含 * 和 #)
邮箱地址
textEmailAddress
邮箱格式键盘(含 @ 和 .)
密码
普通密码
textPassword
隐藏输入内容(显示圆点或星号)
可见密码
textVisiblePassword
显示实际输入的字符(用于密码确认)
数字密码
numberPassword
数字键盘 + 隐藏输入(如支付密码)
格式控制
自动大写单词
textCapWords
每个单词首字母自动大写(如 "Hello World")
自动大写句子
textCapSentences
每句首字母自动大写(默认行为)
自动纠正拼写
textAutoCorrect
自动纠正拼写错误(如 "teh" → "the")
自动完成
textAutoComplete
根据上下文提示补全(需结合代码实现)
其他行为
无输入建议
textNoSuggestions
禁用键盘的输入建议(如网址输入框)
日期
date
日期选择器(弹出日历)
时间
time
时间选择器(弹出时钟)
组合类型(|分隔)
邮箱输入框:邮箱键盘 + 自动大写单词 + 自动纠正
android:inputType="textEmailAddress|textCapWords|textAutoCorrect"
CompoundButton(抽象类)
父类
直接父类Button,父类父类 TextView,View
子类
CheckBox(复选框组件)
父类
直接父类CompoundButton,父类父类TextView,View
XML
基础显示
<CheckBox
android:id="@+id/checkbox1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="选项1" />
android:id="@+id/checkbox1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="选项1" />
会在界面上显示一个带文字“选项1”的复选框(选框+文字的组合)
标签本身就会渲染出一个标准的复选框(包括方框和文字)
也可以自定义文字颜色,大小等等
正式使用
要使用多个CheckBox标签
<CheckBox
android:id="@+id/checkbox1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="选项A" />
<CheckBox
android:id="@+id/checkbox2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="选项B" />
<CheckBox
android:id="@+id/checkbox3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="选项C" />
android:id="@+id/checkbox1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="选项A" />
<CheckBox
android:id="@+id/checkbox2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="选项B" />
<CheckBox
android:id="@+id/checkbox3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="选项C" />
Java
通过findViewById()获取对象cb
boolean b = cb.isChecked();
检查当前复选框是否被勾选
void = cb.setChecked(true/false)
设置当前复选框勾选(true),不勾选(false)
void = cb.toggle()
j将当前复选框的选择状态反选
RadioButton(单选框组件)
父类
直接父类CompoundButton(抽象类),父类父类TextView,View
XML
实现方式:RadioButton + RadioGroup
<!-- 关键:用 RadioGroup 包裹 RadioButton -->
<RadioGroup
android:id="@+id/radio_group"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<RadioButton
android:id="@+id/radio1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="选项A" />
<RadioButton
android:id="@+id/radio2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="选项B" />
<RadioButton
android:id="@+id/radio3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="选项C" />
</RadioGroup>
<RadioGroup
android:id="@+id/radio_group"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<RadioButton
android:id="@+id/radio1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="选项A" />
<RadioButton
android:id="@+id/radio2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="选项B" />
<RadioButton
android:id="@+id/radio3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="选项C" />
</RadioGroup>
RadioGroup是CompoundButton的子类,也是容器组件
Java
RadioGroup特有方法
通过findViewById()获取对象rg
void =
rg.check(int rbid)
设置/选中指定ID的RadioButton(强制让它被选中)
rg.clearCheck()
清除单选按钮的选中状态
int id = rg.getCheckedRadioButtonId
获取当前被选中的按钮id,若无,返回-1
rg.setOriebtation()
设置单选按钮的排列方向
LinearLayout.HORIZONTAL:水平方向
LinearLayout.VERTICAL:垂直方向
Switch(开关组件)
基础实现
<Switch
android:id="@+id/my_switch"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="夜间模式" <!-- 可选:右侧显示的文字 -->
android:textOff="关闭" <!-- 可选:关闭状态的文字(默认"OFF") -->
android:textOn="开启" <!-- 可选:开启状态的文字(默认"ON") -->
android:thumbTint="@color/blue" <!-- 可选:滑块颜色 -->
android:trackTint="@color/gray" <!-- 可选:轨道颜色 -->
/>
android:id="@+id/my_switch"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="夜间模式" <!-- 可选:右侧显示的文字 -->
android:textOff="关闭" <!-- 可选:关闭状态的文字(默认"OFF") -->
android:textOn="开启" <!-- 可选:开启状态的文字(默认"ON") -->
android:thumbTint="@color/blue" <!-- 可选:滑块颜色 -->
android:trackTint="@color/gray" <!-- 可选:轨道颜色 -->
/>
XML配置文件
开关右侧显示的文字(如"夜间模式")
android:text
推荐字符串资源引用
自定义开关开启/关闭时的文字(默认是"ON"/"OFF")
android:textOn/android:textOff
推荐字符串资源引用
滑块(圆形部分)的颜色
android:thumbTint
推荐颜色资源引用
轨道(背景条)的颜色
android:trackTint
推荐颜色资源引用
ToggleButton(基本切换组件)
地位基础,大部分场景被Switch替换,主要为了维护和兼容性。仅支持点击切换,仅支持背景和文字自定义
基础实现
<ToggleButton
android:id="@+id/my_toggle_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textOn="开启" <!-- 自定义开启状态文字(默认"ON") -->
android:textOff="关闭" <!-- 自定义关闭状态文字(默认"OFF") -->
android:background="@drawable/custom_toggle_bg" <!-- 可选:自定义背景 -->
/>
android:id="@+id/my_toggle_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textOn="开启" <!-- 自定义开启状态文字(默认"ON") -->
android:textOff="关闭" <!-- 自定义关闭状态文字(默认"OFF") -->
android:background="@drawable/custom_toggle_bg" <!-- 可选:自定义背景 -->
/>
XML配置属性
开启状态显示的文字(默认"ON")
android:textOn
关闭状态显示的文字(默认"OFF")
android:textOff
自定义背景(可替换默认样式)
android:background
容器组件
ViewGroup(视图容器)
ViewGroup(视图容器)
LinearLayout(线性布局)
子类
RadioGroup(见上 Button的RadioButtom)
TableLayout(表格布局)仅作了解
<ScrollLayout>
<TableLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:stretchColumns="*">
<TableRow>
<Button
android:layout_width="30dp"
android:layout_height="wrap_content"
android:layout_margin="5dp"
android:text="按钮"/>
<Button
android:layout_width="30dp"
android:layout_height="wrap_content"
android:layout_margin="5dp"
android:text="按钮"/>
</TableRow>
<TableRow>
<Button
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"//继承线性布局,支持权重
android:layout_margin="5dp"
android:text="按钮"/>
<Button
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_margin="5dp"
android:text="按钮"/>
</TableRow>
</TableLayout>
</ScrollLayout>
<TableLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:stretchColumns="*">
<TableRow>
<Button
android:layout_width="30dp"
android:layout_height="wrap_content"
android:layout_margin="5dp"
android:text="按钮"/>
<Button
android:layout_width="30dp"
android:layout_height="wrap_content"
android:layout_margin="5dp"
android:text="按钮"/>
</TableRow>
<TableRow>
<Button
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"//继承线性布局,支持权重
android:layout_margin="5dp"
android:text="按钮"/>
<Button
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_margin="5dp"
android:text="按钮"/>
</TableRow>
</TableLayout>
</ScrollLayout>
属性
android:stretchColumns(指定列能伸展)
android:shrinkColumns(指定列能收缩)
android:collapseColumns(指定列隐藏)
XML
容器XML属性
主轴方向
android:orientation
horizontal(水平)
vertical(垂直)
容器内组件对齐方式
android:gravity
top,bottom,left,right
center,start,end
容器内组件XML属性
组件宽高
android:layout_width
android:layout_hright
android:layout_hright
match_parent / fill_parent (占满父容器所有空间)
wrap_content (占用内容所需空间)
100sp ,100dp(固定大小)
组件对齐方式
android:layout_gravity
top,bottom,left,right
center,start,end
top|right(右上)
占用容器剩余空间的比例
android:layout_weight
如果是垂直布局,要设置layout_height为0dp才生效
水平 width
水平 width
组件外边距
android:layout_margin
Top
……
Top
……
简写格式遵循值复制原则,不支持简写顺时针原则
Java
动态调整布局方向
ll.setOrientation()
LinearLayout.HORIZONTAL:水平方向
LinearLayout.VERTICAL:垂直方向
RelativeLayout(相对布局)
XML
容器XML属性
布局冲突
避免在容器中使用android:gravity,它会导致约束出现冲突
容器内组件XML属性
将其定位到指定组件上方
android:layout_above
属性值为指定容器的id
将其定位到指定组件下方
android:layout_below
属性值为指定容器的id
将组件定位到指定位置(值为true时)
android:layout_alignParentTop
Bottom
Left
Right
Bottom
Left
Right
定位到父容器组件的最上方
下
左
右
下
左
右
android:layout_centerInParent
定位到父容器组件的中央
重叠
若有多个组件同时相对一个组件的相同方位,会出现重叠
Java
见View
FrameLayout(层叠布局)
特征
布局最简单,理解最麻烦(需要写明特征)
子视图按添加顺序层叠排列(后添加的视图覆盖先添加的视图)
适用于需要“叠加显示”或“层叠效果”的场景
适用于需要“叠加显示”或“层叠效果”的场景
图片上叠加文字,对话框/弹出框,卡片堆叠效果
FrameLayout 的核心特点
1. 默认位置:所有子视图默认放置在布局的左上角(坐标为(0,0))
2. 层叠顺序:子视图按照在 XML 中声明的顺序(或添加顺序)层叠,后面的视图覆盖在前面的视图上(类似堆叠卡片)
3. 自定义位置:可以通过 `layout_gravity` 属性调整子视图在 FrameLayout 中的位置(如居中、靠右等)
4. 前景视图:FrameLayout 支持 `foreground` 属性,可以在所有子视图之上绘制一个前景(如半透明蒙版)
5. 尺寸控制:子视图的尺寸默认是 `wrap_content`,但可以通过 `layout_width` 和 `layout_height` 设置为 `match_parent` 或固定值
1. 默认位置:所有子视图默认放置在布局的左上角(坐标为(0,0))
2. 层叠顺序:子视图按照在 XML 中声明的顺序(或添加顺序)层叠,后面的视图覆盖在前面的视图上(类似堆叠卡片)
3. 自定义位置:可以通过 `layout_gravity` 属性调整子视图在 FrameLayout 中的位置(如居中、靠右等)
4. 前景视图:FrameLayout 支持 `foreground` 属性,可以在所有子视图之上绘制一个前景(如半透明蒙版)
5. 尺寸控制:子视图的尺寸默认是 `wrap_content`,但可以通过 `layout_width` 和 `layout_height` 设置为 `match_parent` 或固定值
常见用途
1.作为容器:用于放置单个视图(如全屏图片)
2. 层叠视图:用于需要重叠显示的 UI 元素(如图标上的角标、视频播放器的控制按钮)
3. 占位布局:作为动态添加视图的容器(如 Fragment 的容器)
4. 前景效果:通过前景属性实现统一的遮罩效果
2. 层叠视图:用于需要重叠显示的 UI 元素(如图标上的角标、视频播放器的控制按钮)
3. 占位布局:作为动态添加视图的容器(如 Fragment 的容器)
4. 前景效果:通过前景属性实现统一的遮罩效果
XML
容器XML属性
FrameLayout特有
android:foreground
在所有内容前添加一个前景层,可以是图片,颜色,主题资源
容器内组件XML属性
组件宽高
android:layout_width
android:layout_hright
android:layout_hright
默认
match_parent / fill_parent (占满父容器所有空间)
wrap_content (占用内容所需空间)
100sp ,100dp(固定大小)
组件对齐方式
android:layout_gravity
top,bottom,left,right
cente,start,end
top|right(右上)
裁剪子视图
android:clipChildren
默认为true
裁剪超出容器范围的子视图
false
显示完整的子视图(即使超出容器)
透明度
android:alpha
<!-- 0.0(全透明) ~ 1.0(不透明) -->
Java
见View
子类
ScrollView
设计目标
为内容超过屏幕尺寸的视图提供滚动功能
功能
完全继承FrameLayout的XML属性,额外实现了垂直滚动功能
通常使用垂直布局的LinearLayout作为ScrollView的子组件
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
</LinearLayout>
高度的设置是有必要重视的,为了避免与滚动功能冲突,不能设置为match_parent
android:layout_width="match_parent"
android:layout_height="wrap_content">
</LinearLayout>
XML
容器XML属性
滚动条
设置滚动条的显示类型
android:scrollbars
vertical(垂直滚动条)
horizontal(水平滚动条)
none(隐藏)
设置滚动条的样式
android:scrollbarStyle
insideInset(滚动条嵌入内容内)
outsideInset(滚动条在内容外)
设置滚动条的宽度
android:scrollbarSize
8dp(常见滚动条宽度)
自定义垂直滚动条的滑块样式(需指定 Drawable)
android:scrollbarThumbVertical
@drawable/custom_scroll_thumb
滚动行为
设置“过度滚动”的视觉效果(超出内容边界时的弹性效果)
android:overScrollMode
默认
(即使不显式设置scrollmode也默认有)
(即使不显式设置scrollmode也默认有)
ifContentScrolls(内容可滚动时显示)
never(禁用)
always
设置滚动时边缘渐隐效果的宽度(像素值,仅当 overScrollMode 启用时有效)
android:fadingEdgeLength
32dp(边缘渐隐长度)
强制拉伸
android:fillViewport
true
子视图的高度会被强制拉伸以填满 ScrollView 的可见区域(即使内容不足)
Java
子类
HorizontalScrollView
设计目标
提供水平滚动功能,与 ScrollView(垂直滚动)形成互补
功能
完全继承FrameLayout和ScrollView的XML属性,额外实现了水平滚动功能
ConstraintLayout(约束布局)
XML
容器内组件XML属性
相对位置
app:layout_constraint[当前视图的边界]_to[目标视图的边界]Of="[目标视图的ID]
[当前视图的边界]:可以是 Left、Right、Top、Bottom、Start、End 等。
[目标视图的边界]:同样可以是 Left、Right、Top、Bottom、Start、End 等。
[目标视图的ID]:可以是另一个视图的ID,或者 parent(表示父布局)
[目标视图的边界]:同样可以是 Left、Right、Top、Bottom、Start、End 等。
[目标视图的ID]:可以是另一个视图的ID,或者 parent(表示父布局)
app:layout_constraintLeft_toLeftOf
将当前组件的左边对齐指定组件的左边,如果垂直方向没有约束,通常组件会出现重叠
app:layout_constraintLeft_toRightOf
将当前组件的左边对齐指定组件的右边
外边距(View共有属性)
android:layout_margin
Top
Bottom
Left
Right
Start
End
Top
Bottom
Left
Right
Start
End
推荐使用dp
约束布局的居中和位置偏移
居中(约束布局)
水平
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintHorizontal_bias="0.5"
垂直
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintVretical_bias="0.5"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintVretical_bias="0.5"
当前布局不推荐layout_gravity或父容器设置gravity
app:layout_constraintHorizontal_bias
app:layout_constraintVretical_bias
app:layout_constraintVretical_bias
默认值为0.5
0.3就是向左偏置30%,组件的左边缘距离父容器左边缘30%
当bias=0.0时,组件会紧靠左边(即组件的左边缘与父容器的左边缘对齐)
当bias=1.0时,组件会紧靠右边(即组件的右边缘与父容器的右边缘对齐)
当bias=0.5时,组件在剩余空间的中间,也就是组件在父容器中水平居中
当bias=1.0时,组件会紧靠右边(即组件的右边缘与父容器的右边缘对齐)
当bias=0.5时,组件在剩余空间的中间,也就是组件在父容器中水平居中
本质是分配剩余空间,只有在绑定约束关系(相对位置) 时生效
弧形定位
app:layout_constraintCircle
CircleRadius
CircleAngle
CircleRadius
CircleAngle
将当前组件相对指定组件A,app:layout_constraintCircle="@id/buttonA"
设置指定组件A的半径,app:layout_constraintCircleRadius="100dp"
设置当前组件相对A的角度,app:layout_constraintCircleAngle="45"
将当前组件定位到以A为圆心,半径为100dp,角度为45度的圆边上
设置指定组件A的半径,app:layout_constraintCircleRadius="100dp"
设置当前组件相对A的角度,app:layout_constraintCircleAngle="45"
将当前组件定位到以A为圆心,半径为100dp,角度为45度的圆边上
尺寸约束
在宽或高设置为wrap_content后生效
android:maxWidth
maxHeight
minWidth
minHeight
maxHeight
minWidth
minHeight
推荐dp
固定比例
app:layout_constraintDimensionRatio`
"H,16:9" <!-- H表示以宽度为基准,高度按比例;也可以写W表示以高度
百分比
app:layout_constraintWidth_percent
0.7,70%
核心
通过设置宽或高为0dp,再通过约束,例如左边对齐父容器左边,右边对齐右边来实现宽度
实现百分比布局
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintWidth_percent="0.7" <!-- 70%宽度 -->
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>
android:layout_height="wrap_content"
app:layout_constraintWidth_percent="0.7" <!-- 70%宽度 -->
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>
Java
AdapterView(抽象类)
特征
抽象类不可直接实例化,所以它不能作为一个容器组件
通过适配器操作子类
子类像基本组件一样设置在容器组件中
Adapter接口(适配器)
实现类
BaseAdapter类(最简单)
ArrayAdapter类
SimpleAdapter类
SimpleCursorAdapter类
实现方法
public int getCount(){}
返回要显示的数据集中的数据总数(通常是数组或集合的长度)
public Object getItem(int position){}
返回要显示的数据集中指定位置的数据对象(通常是数组或集合索引)
public long getItemId(int position){}
返回指定位置的数据ID
核心
public View getView(int position , View v , ViewGroup vg){}
//int position:当前需要显示的数据位置(0,1,2...)
//View v:可复用的旧视图(需要代码实现)
//ViewGroup vg:父容器(ListView)
//View v:可复用的旧视图(需要代码实现)
//ViewGroup vg:父容器(ListView)
获取当前点击的位置
组件.setTag(index); // 设置当前视图对应的位置
int clickedIndex = (int) 对象.getTag(); // 获取视图当前绑定的位置
int clickedIndex = (int) 对象.getTag(); // 获取视图当前绑定的位置
不直接使用传入的position的原因是视图复用时,
你点击新的指向的还是旧的
你点击新的指向的还是旧的
下来框另外样式
public View getDropDownView()
默认情况下会使用getView相同的布局
AdapterView子类
ListView组件
通过Adapter操作显示内容
Spinner组件
通过Adapter操作显示内容
GridView组件
面向对象设置XML
内容对齐方式
android:gravity="center"
指定每列宽度
android:columnWidth
90dp
指定每行列数
android:numColumns
auto_fit,自动适用
整数值,3代表三列
剩余空间分配
android:stretchMode
none,不分配
columnWidth,每列自动放大
可能会影响内容
spacingWidth,列与列之间的间隔放大(首尾无间隙)
spacingWidthUniform,列与列之间平均分配(首尾有间隙)
间隔
android:horizontalSpacing="10dp"
android:verticalSpacing="10dp"
android:verticalSpacing="10dp"
对象
默认会填满每个GridView格子
LayoutInflater类
功能
在getView中使用,布局"充气机",负责将 XML 布局转换为实际的 View 对象
方法
静态方法,获取对象
LayoutInflater li = LayoutInflater.from(context);
li.inflate(int resource, ViewGroup root, boolean attachToRoot)
resource
要加载的布局资源 ID(adapter_item.xml),确定列表项的外观
root
父容器 (ListView),提供正确的布局参数
就是指向你那个使用了AdapterView子类的文件中的那个子类对象
attachToRoot
是否立即附加,必须为 false,否则会重复添加视图
实现思路
AdapterView子类——Adapter——显示内容,
所以我们需要一个包含AdapterView子类的xml,一个显示内容布局的xml,
一个实现Adapter的java,一个为AdapterView子类设置适配器的java
所以我们需要一个包含AdapterView子类的xml,一个显示内容布局的xml,
一个实现Adapter的java,一个为AdapterView子类设置适配器的java
AndroidManifest.xml(简单)
指定.AdapterActivity
adapter.xml(简单)
设置AdapterView实现类
<ListView
android:id="@+id/lv"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
android:id="@+id/lv"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
AdapterActivity.java(困难)
设置布局setContentView(R.layout.adapter)
获取adapter布局中实现类的对象ListView lv = this.findViewById(R.id.lv);
实例化实现Adapter接口的类的对象MyAdapter ma = new MyAdapter(this,R.layout.adapter_item);
// Context this 是当前 Activity 实例 (AdapterActivity.this)
// Context this 是当前 Activity 实例 (AdapterActivity.this)
this指代当前文件的对象上下文
是LayoutInflater.from(context);中的参数
是LayoutInflater.from(context);中的参数
设置适配器lv.setAdapter(ma);
可以发现AdapterView主要操作的
就是这俩个文件
就是这俩个文件
在AdapterActivity.java的第三步,第四步使用
MyAdapter.java(最困难)
实现Adapter接口,实现getCount,getItem,getItemId,getView方法
构造方法中获取充气机对象inflater = LayoutInflater.from(context);
将XML转为View对象LinearLayout ll = (LinearLayout)inflater.inflate(adapter_item,viewGroup,false)
在AdapterActivity.java的第三步使用
在MyAdapter.java中的布局充气机中转换为View对象
在MyAdapter.java中的布局充气机中转换为View对象
adapter_item.xml(简单)
设置AdapterView实现类内的具体布局
<ImageView
android:id="@+id/itempic"
android:layout_width="60dp"
android:layout_height="60dp"
android:contentDescription=""/>
<TextView
android:id="@+id/itemname"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_vertical">
</TextView>
android:id="@+id/itempic"
android:layout_width="60dp"
android:layout_height="60dp"
android:contentDescription=""/>
<TextView
android:id="@+id/itemname"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_vertical">
</TextView>
常用案例
获取应用列表
1
在接口实现类的构造方法中,使用上下文对象调用
PackageManager pm = c.getPackageManager()
PackageManager pm = c.getPackageManager()
2
获取应用列表
List<ApplicationInfo> list = pm.getInstalledApplications(PackageManager.GET_META_DATA);
getInstalledPackages(int flags): 返回 List<PackageInfo>。PackageInfo 包含的信息比 ApplicationInfo 更全(如权限、版本号、Activity/Service 等组件信息),但如果你只需要应用列表(名字、图标、包名),它比 getInstalledApplications 开销更大。
3
遍历添加内容
for(ApplicationInfo ai : list){
HashMap<String, Object> hm = new HashMap<>();
hm.put("icon",ai.loadIcon(pm));
hm.put("appName",ai.loadLabel(pm));
hm.put("packageName",ai.packageName);
items.add(hm);
}
HashMap<String, Object> hm = new HashMap<>();
hm.put("icon",ai.loadIcon(pm));
hm.put("appName",ai.loadLabel(pm));
hm.put("packageName",ai.packageName);
items.add(hm);
}
如果使用getInstalledPackages(int flags)
则ai.applicationInfo.loadIcon(pm)
则ai.applicationInfo.loadIcon(pm)
其他常用组件
生活
日历
CalendarView
日期
DatePicker
时间
TimePicker
交互
进度条
ProgressBar
图片按钮
ImageButtob
分隔组件
Space
提升
视图动画
ViewFilpper
以动画形式切换显示组件
基于View动画
基于View动画
网页视图
WebView
嵌入Web网页
样式与主题与背景
样式资源(style.xml)
基本路径
res/value/style.xml
XML
<style name="BoldText">
<item name="android:textSize">@dimen/size</item>
</style>
<item name="android:textSize">@dimen/size</item>
</style>
继承
<style name="BoldText" parent="@style/father">
BoldText继承father的所有样式,
并且替换父样式的自身定义样式
并且替换父样式的自身定义样式
name
在定义中,使用_下划线来分隔单词
使用.小数点来分隔也是可行的,为了统一建议都使用.
在引用中,使用.小数点来分隔
引用
XML
style="@style/BoldText"
Java
R.style.BoldText
注意要点
对于按钮
全限定名
android.widget.Button
自定义较简单优秀
需要手动实现所有效果,逐渐淘汰
简写
Button
自动获得 Material 更新,默认拥有圆角边框,点击效果
搭配backgroundTint
只改变背景颜色,不影响其他
使用background
无法改变背景颜色还会导致圆角点击效果失效
主题资源(theme.xml)
基本路径
res/value/theme/theme.xml
先继承已经实现必要样式的系统内置主题,再进行自定义
<resources>
<!-- 浅色主题 -->
<style name="Theme.MyApp.Light" parent="Theme.MaterialComponents.Light">
<!-- 自定义颜色 -->
<item name="colorPrimary">@color/custom_primary</item>
<item name="colorPrimaryVariant">@color/custom_primary_dark</item>
<item name="colorOnPrimary">@color/white</item>
<!-- 背景色 -->
<item name="android:windowBackground">@color/custom_background_light</item>
<!-- 禁用系统主题覆盖 -->
<item name="materialThemeOverlay">@null</item>
</style>
</resources>
<!-- 浅色主题 -->
<style name="Theme.MyApp.Light" parent="Theme.MaterialComponents.Light">
<!-- 自定义颜色 -->
<item name="colorPrimary">@color/custom_primary</item>
<item name="colorPrimaryVariant">@color/custom_primary_dark</item>
<item name="colorOnPrimary">@color/white</item>
<!-- 背景色 -->
<item name="android:windowBackground">@color/custom_background_light</item>
<!-- 禁用系统主题覆盖 -->
<item name="materialThemeOverlay">@null</item>
</style>
</resources>
在AndroidManifest.mxl中配置主题
android:theme="@style/Theme.MyApp.Light"
自定义背景(drawable.xml)
定义
<?xml version="1.0" encoding="utf-8"?>
<!-- 使用 shape 包裹 gradient -->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<!-- 添加圆角 -->
<corners android:radius="50dp" />
<!-- 渐变定义 -->
<gradient
android:startColor="#FF9800"
android:endColor="#FF5722"
android:angle="45" />
<!-- 可选:添加填充 -->
<solid android:color="#FFFFFF" />
<!-- 可选:边框 -->
<stroke
android:width="5dp"
android:color="#000"/>
</shape>
<!-- 使用 shape 包裹 gradient -->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<!-- 添加圆角 -->
<corners android:radius="50dp" />
<!-- 渐变定义 -->
<gradient
android:startColor="#FF9800"
android:endColor="#FF5722"
android:angle="45" />
<!-- 可选:添加填充 -->
<solid android:color="#FFFFFF" />
<!-- 可选:边框 -->
<stroke
android:width="5dp"
android:color="#000"/>
</shape>
非对称圆角
<corners
android:topLeftRadius="12dp"
android:topRightRadius="12dp"
android:bottomLeftRadius="0dp"
android:bottomRightRadius="0dp" />
<corners
android:topLeftRadius="12dp"
android:topRightRadius="12dp"
android:bottomLeftRadius="0dp"
android:bottomRightRadius="0dp" />
渐变背景
<gradient
android:startColor="#FF9800"
android:endColor="#FF5722"
android:angle="45" />
<gradient
android:startColor="#FF9800"
android:endColor="#FF5722"
android:angle="45" />
渐变背景和背景填充最好只存在一个,同时存在会导致覆盖
应用
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/rounded_border"
android:text="圆角边框文本" />
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/rounded_border"
android:text="圆角边框文本" />
<shape>
功能
用于定义一个几何形状,可以用作视图的背景
要点
必须指定 xmlns:android="http://schemas.android.com/apk/res/android" 命名空间
通过 android:shape 属性指定形状的类型
rectangle:矩形(默认值)
oval:椭圆
line:水平线
ring:环形
属性
设置矩形的圆角半径
<corners>
android:radius="12dp"
android:topLeftRadius
android:topRightRadius
android:bottomLeftRadius
android:bottomRightRadius
android:topLeftRadius
android:topRightRadius
android:bottomLeftRadius
android:bottomRightRadius
设置矩形的渐变填充
<gradient>
android:startColor="#FF9800"
android:centerColor="#FFFF00"
android:endColor="#FF5722"
android:angle="45"
android:type="linear"
android:centerColor="#FFFF00"
android:endColor="#FF5722"
android:angle="45"
android:type="linear"
设置矩形的纯色填充
<solid>
android:color="#FFFFFF"
设置矩形的边框(线宽和颜色)
<stroke>
android:width="5dp"
android:color="#000"
android:color="#000"
自定义控件和库控件
自定义控件 ✅ 必须写 <com.example.MyView> 必须包含完整包名
支持库/Material 控件 ✅ 必须写 <androidx.cardview.widget.CardView> 不在默认命名空间
菜单接口
(android.view.menu包)
(android.view.menu包)
系统内部实现类
(如 MenuBuilder)
(如 MenuBuilder)
XML
创建菜单列表布局
res/menu/menuName.xml
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/menu_01"
android:icon="@mipmap/ic_launcher_round"
android:orderInCategory="100"
app:showAsAction="ifRoom|withText"
android:title="手机号"/>
</menu>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/menu_01"
android:icon="@mipmap/ic_launcher_round"
android:orderInCategory="100"
app:showAsAction="ifRoom|withText"
android:title="手机号"/>
</menu>
XML配置属性
图标
android:icon
选项名称
android:title
显示行为
app:showAsAction
单行为
never
(推荐)
(推荐)
始终显示在溢出菜单(三点菜单)中
ifRoom
有空间时显示在 ActionBar(三点菜单外),否则进溢出菜单
always
始终显示在 ActionBar(无视空间)
withText
强制显示文字标签(需与其它值组合)
多行为
单行为|单行为
ifRoom|withText
(推荐)
(推荐)
优先显示图标+文字,
空间不足时:先尝试只显示图标
空间仍不足则移入溢出菜单
空间不足时:先尝试只显示图标
空间仍不足则移入溢出菜单
优先级
android:orderInCategory
值越小,优先级越高
Java
将列表绑定到Layout并显示出来
public boolean on-Create-Options-Menu(Menu menu){//内部实现类对象
this.getMenuInflater().inflate(R.menu.menus,menu);//将 XML 中定义的菜单项(如 <item>),解析并添加到menu对象中
return true;
}
this.getMenuInflater().inflate(R.menu.menus,menu);//将 XML 中定义的菜单项(如 <item>),解析并添加到menu对象中
return true;
}
对列表的选项绑定响应
public boolean on-Options-Item-Selected(MenuItem mi){
int id = mi.getItemId();
switch(id){//利用switch对比响应选择
case R.id.xxx:
Toast.makeText(this,"选择了xxx",Toast.LENGTH.LONG).show();
default:
无
}
switch的case值有问题,推荐使用if-else语句
return super.onOptionsItemSelected(mi);//父类包含基本实现逻辑
}
int id = mi.getItemId();
case R.id.xxx:
Toast.makeText(this,"选择了xxx",Toast.LENGTH.LONG).show();
default:
无
}
switch的case值有问题,推荐使用if-else语句
return super.onOptionsItemSelected(mi);//父类包含基本实现逻辑
}
视觉优化
动画
View动画(transform)
(android.view.animation包)
(android.view.animation包)
补间动画
补间动画资源
res/anim/my_anim.xml
定义动画(XML)
类型
<set>混合类型,<rotate>旋转,<scale>缩放,<translate>位移,<alpha>透明度
复合动画
<set>
<rotate />
<alpha />
……
<set></set>
</set>
<rotate />
<alpha />
……
<set></set>
</set>
定义动画
<?xml version="1.0" encoding="utf-8"?>
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
android:fromDegrees="0"
android:toDegrees="90"
android:pivotX="0%"
android:pivotY="100%"
android:duration="3000"
android:repeatCount="infinite"
android:repeatMode="reverse"
android:fillAfter="true"
android:interpolator="@android:anim/linear_interpolator"/>
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
android:fromDegrees="0"
android:toDegrees="90"
android:pivotX="0%"
android:pivotY="100%"
android:duration="3000"
android:repeatCount="infinite"
android:repeatMode="reverse"
android:fillAfter="true"
android:interpolator="@android:anim/linear_interpolator"/>
XML配置属性
通用XML配置属性
变形原点
android:pivotX="float"
android:pivotY="float"
时间
延迟执行
android:startoffset="int"
执行时间
android:duration="int"
单位毫秒
动画重复
重复次数
android:repeatCount="int"
infinite(无限循环)
重复行为
android:repeatMode
restart(从头到尾)
reverse(从尾到头)
结束样式
android:fillAfter
true(保持动画最后一帧)
false(恢复初始帧)
缓动函数(动画曲线,插值器)
android:interpolator
@android:anim/accelerate_decelerate_interpolator
先加速后减速(默认)
@android:anim/linear_interpolator
匀速
专属XML配置属性
rotate(旋转角度)
android:fromDegrees="float"
android:toDegrees="float"
android:toDegrees="float"
scale(缩放大小)
android:fromXScale="float"
android:toXScale="float"
android:fromYScale="folat"
android:toYScale="float"
android:toXScale="float"
android:fromYScale="folat"
android:toYScale="float"
translate(位移距离)
android:fromXDelta="float"
android:toXDelta="float"
android:fromYDelta="float"
android:toYDelta="float"
android:toXDelta="float"
android:fromYDelta="float"
android:toYDelta="float"
alpha(透明度变化)
android:fromAlpha="float"
android:toAlpha="float"
android:toAlpha="float"
使用动画(Java)
Animation rotate = AnimationUtils.loadAnimation(this,R.anim.my_anim);
组件.startAnimation(rotate);
组件.startAnimation(rotate);
帧动画
资源
帧动画资源
res/drawable/drawable_anim.xml
图片资源
res/drawable/lists.png
定义动画(XML)
定义动画draw._anim.xml)
<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
android:oneshot="false">
<item android:drawable="@drawable/one" android:duration="2000"/>
<item android:drawable="@drawable/two" android:duration="2000"/>
<item android:drawable="@drawable/three" android:duration="2000"/>
<item android:drawable="@drawable/four" android:duration="2000"/>
</animation-list>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
android:oneshot="false">
<item android:drawable="@drawable/one" android:duration="2000"/>
<item android:drawable="@drawable/two" android:duration="2000"/>
<item android:drawable="@drawable/three" android:duration="2000"/>
<item android:drawable="@drawable/four" android:duration="2000"/>
</animation-list>
XML配置属性
集合配置属性
循环
android:oneshot
true(一次性执行动画)
false(循环执行动画)
项XML配置属性
<item android:drawable="@drawable/four" android:duration="2000"/>
指定图片
android:drawable
动画时间
android:duration
使用动画(Java)
iv = findViewById(R.id.zhen_img01);
//获取对象AnimationDrawable ad = (AnimationDrawable) iv.getDrawable();
zhen_btn01.setOnClickListener((v)->{
ad.start();//开始
});
zhen_btn02.setOnClickListener(v->{
ad.stop();//停止
});
//获取对象AnimationDrawable ad = (AnimationDrawable) iv.getDrawable();
zhen_btn01.setOnClickListener((v)->{
ad.start();//开始
});
zhen_btn02.setOnClickListener(v->{
ad.stop();//停止
});
属性动画(animation)
(android.animation包)
(android.animation包)
系统交互
Intent(消息传递对象)
理解
各个Activity.java
在AndroidManifest.xml中声明自己的action,gatecory,data等等
以程序入口的Activity.java为例
在文件中对各个组件绑定事件,
当事件执行时,实例化Intent并设置参数
当事件执行时,实例化Intent并设置参数
Intent会依靠获得的参数在AndroidManifest.xml中匹配
当匹配成功时,对像会启动运行对应参数的activity.java
当匹配成功时,对像会启动运行对应参数的activity.java
只是匹配器,本身并不实现对应条件描述的功能,只是对比对象被设置的数据与AndroidManifest.xml中的进行匹配
Intent解析(Java)
如果在实例化Intent对象时未设置
action
匹配AndroidManifest.xml中所有activity
gatecory
只能匹配AndroidManifest.xml中未设置gatecory的activity
data
只能匹配AndroidManifest.xml中未设置data的activity
基础使用
显式启动
AndroidManifest.xml
<activity
android:name=".IntIMGActivity"
android:exported="false">
</activity>
android:name=".IntIMGActivity"
android:exported="false">
</activity>
不设置(显式启动的关键)
<intent-filter>
<action android:name="android.intent.action.MAIN" />//包含该Action的Activity.java是应用程序的入口
<category android:name="android.intent.category.LAUNCHER" />//通常与Action.MAIN搭配出现
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.MAIN" />//包含该Action的Activity.java是应用程序的入口
<category android:name="android.intent.category.LAUNCHER" />//通常与Action.MAIN搭配出现
</intent-filter>
Java实例化
Intent i = new Intent(this, IntIMGActivity.class)
从哪里跳转(当前 Activity) 要跳转到哪里(目标 Activity)
从哪里跳转(当前 Activity) 要跳转到哪里(目标 Activity)
this:当前上下文(Context),指代当前的java文件
IntIMGActivity.class:目标Activity的类对象,明确指定要跳转到哪个页面(IntIMGActivity.java控制的界面)
隐式启动
AndroidManifest.xml
<activity
android:name=".IntIMGActivity"
android:exported="false">
</activity>
android:name=".IntIMGActivity"
android:exported="false">
</activity>
设置(隐式启动的关键),设置过滤器
<intent-filter>
<action android:name="yinshiqidong" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
<intent-filter>
<action android:name="yinshiqidong" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
Java实例化与设置
Intent i = new Intent("yinshiqidong");
也可以通过i.setAction("yingshiqidong)
i.addCategory(Intent.CATEGORY_DEFAULT);
开启新界面
this.startActivity(i);
startActivity():Activity 类的方法,用于启动新的 Activity
this:指代当前的 IntentActivity1(可以省略)
属性
action(启动activity)
XML属性
<action android:name=""/>
使用字符串
核心
应用程序的入口
(必须与Gatecory.Launcher搭配)
(必须与Gatecory.Launcher搭配)
android.intent.action.MAIN
Java
使用
在实例化Intent对象时作为参数
Intent i = new Intent("yinshiqidong");
通过i.setAction()方法
i.setAction("yinshiqidong")
Java参数值
预定义使用常量
(只是声明具有什么功能,对应的功能是在activity.java内实现的!)
(只是声明具有什么功能,对应的功能是在activity.java内实现的!)
核心
Intent.ACTION_MAIN
查看/显示数据
Intent.ACTION_VIEW (android.intent.action.VIEW)
分享数据到其他应用
Intent.ACTION_SEND (android.intent.action.SEND)
打开拨号界面(不直接拨号)
Intent.ACTION_DIAL (android.intent.action.DIAL)
直接拨打电话
Intent.ACTION_CALL (android.intent.action.CALL)
发送短信/彩信
Intent.ACTION_SENDTO (android.intent.action.SENDTO)
请求从其他应用获取内容
ACTION_GET_CONTENT (android.intent.action.GET_CONTENT)
自定义或预定义使用字符串
"yinshiqidong"
category(activity分类)
XML属性
<category android:name=""/>
核心
android.intent.gatecory.LAUNCHER
(必须与Action.MAIN搭配)
android.category.DEFAULT
BROWSABLE
OPENABLE
BROWSABLE
OPENABLE
Java方法
i.addCategory(Intent.CATEGORY_);
核心
标识应用的主入口点
(必须与Action.MAIN搭配)
(必须与Action.MAIN搭配)
Intent.GATEGORY_LAUNCHER
launcher
允许通过隐式Intent启动
Intent.CATEGORY_DEFAULT
default
AndroidManifest.xml中,要被启动的activity中必须声明该Gatecory
不然在设置了隐式Intent的java中的功能例如按键,点击后会被AndroidManifest阻挡
不然在设置了隐式Intent的java中的功能例如按键,点击后会被AndroidManifest阻挡
声明应用可以作为设备主屏幕
Intent.GATEGORY_HOME
home
允许通过浏览器链接启动应用
Intent.CATEGORY_BROWSABLE
browsable
指示返回的数据可被打开
Intent.CATEGORY_OPENABLE
openable
data
标准形式
scheme://host:port/path
scheme://host:port/pathPattern
scheme://host:port/pathPrefix
XML属性
<data android:
scheme(协议)
http: 超文本传输协议 (用于网络资源)
https: 安全的超文本传输协议
content: Android 特有的协议,表示该资源由 Content Provider 提供 (最常用)
file: 本地文件系统资源
ftp: 文件传输协议
package: 用于标识应用程序包
https: 安全的超文本传输协议
content: Android 特有的协议,表示该资源由 Content Provider 提供 (最常用)
file: 本地文件系统资源
ftp: 文件传输协议
package: 用于标识应用程序包
host(主机)
唯一标识提供资源或服务的实体。 在 content:// URI 中,它通常就是 Content Provider 的 Authority。
port(端口)
在同一 host 上区分不同的服务端点或数据集合。在 content:// URI 中,它不是网络端口,而是一个虚拟端口。
path(路径)
精确指定 Content Provider 内部管理的特定数据项、数据集或操作端点
pathPattern(路劲模式)
使用简单的通配符进行更灵活的路径匹配。
*:匹配它前面的字符的 0 次或多次出现。(注意:不是通配任意字符串!) 例如 a* 可以匹配 a, aa, aaa,但不能匹配 b。
*:匹配 任意字符 (.) 的 0 次或多次出现 (*)。这才是真正的“匹配任意字符串”的通配符。
\:转义字符(如要匹配 * 或 . 本身)。
*:匹配 任意字符 (.) 的 0 次或多次出现 (*)。这才是真正的“匹配任意字符串”的通配符。
\:转义字符(如要匹配 * 或 . 本身)。
pathPrefix(路劲前缀)
匹配以指定字符串开头的路径
pathPrefix="/images/" 会匹配:
/images/cat.jpg
/images/dog.png
/images/photos/profile.jpg
/images/cat.jpg
/images/dog.png
/images/photos/profile.jpg
mimeType(MIME类型)
标识数据的格式和性质。
它告诉系统或应用程序如何处理接收到的数据
它告诉系统或应用程序如何处理接收到的数据
type/subtype
type: 主要类别 (如 text, image, audio, video, application)。
subtype: 特定格式 (如 plain, html, jpeg, png, pdf, json)。
通配符:*/* (匹配任何类型), image/* (匹配任何图片类型)。
type: 主要类别 (如 text, image, audio, video, application)。
subtype: 特定格式 (如 plain, html, jpeg, png, pdf, json)。
通配符:*/* (匹配任何类型), image/* (匹配任何图片类型)。
Java方法
java设置是为Intent添加匹配条件,用于匹配AndroidManifest.xml符号的activity
设置data属性
Intent i = new Intent(String action , Uri data)
i.setData(Uri data)
会清除之前设置的mimeType,在不关心类型时使用(打开一个网页)
设置mimeType属性
i.setType(String mimeType)
会清除之前设置的data,只需要指定数据的MIME类型而不需要具体URI时使用
(打开图片或文件,分享文本)
(打开图片或文件,分享文本)
链式调用
i.setData(Uri data).setType(String mimeType)
不会互相清除
同时设置data和mimeType属性(推荐)
i.setDataAndType(Uri data , String mimeType)
extra
Java
功能
将附加数据传递到被启动的activity中
形式
key/value
key是String类型
value是Java的基本类型,也可以通过实现接口传入读取对象数据类型
传递到被启动activity中
i.putExtra("productName" , "iPhone");
i.putExtra("ProductAmount" , 100);
在被启动的activiity中获取
获取发送数据的Intent
Intent i = getIntent()
String iPhone = i .getStringExtra("productName");
int i = i.getIntExtra("ProductAmount");
获取返回结果(Java)
旧
(已标废弃)
(已标废弃)
通过i.startActivityForResult()启动而非通过i.stratActivity()
i.startActivityForResult()
源Ac.java
this.startActivityForResult(i,1000);
this指当前类对象,i是Intent对象,1000是请求码
请求码 结果码 被启动ac的intent对象
public void onActivityResult(int requestCode , int resultCode, Intent data){
super.onActivityResult(requestCode , resultCode , data);
//调用已经实现核心框架的父类方法而不是重写
该方法内只实现判断返回值的逻辑
}
public void onActivityResult(int requestCode , int resultCode, Intent data){
super.onActivityResult(requestCode , resultCode , data);
//调用已经实现核心框架的父类方法而不是重写
该方法内只实现判断返回值的逻辑
}
请求码(源ac设置) 结果码(被启动ac传递) //双重验证
if ((requestCode == 1000)&&(resultCode == Activity.RESULT_OK)){
String stu = data.getStringExtra("result");//看属性-extra,获取返回值
TextView returns = this.findViewById(R.id.returns);
returns.setText(stu);//参数为String类型
}
if ((requestCode == 1000)&&(resultCode == Activity.RESULT_OK)){
String stu = data.getStringExtra("result");//看属性-extra,获取返回值
TextView returns = this.findViewById(R.id.returns);
returns.setText(stu);//参数为String类型
}
被启动Ac.java
im1.setOnClickListener((v)->{//对某组件的点击事件绑定
Intent i = new Intent();
i.putExtra("result" , "我是返回值");
结果码 Intent对象
this.setResult(Activity.RESULT_OK , i); //设置结果码和携带返回结果的Intent对象
this.finish();//关闭当前Activity界面
});
Intent i = new Intent();
i.putExtra("result" , "我是返回值");
结果码 Intent对象
this.setResult(Activity.RESULT_OK , i); //设置结果码和携带返回结果的Intent对象
this.finish();//关闭当前Activity界面
});
新
ActivityResultLauncher
源Ac.java
声明
private ActivityResultLauncher<Intent> activityLauncher;
private ActivityResultLauncher<String> permissionLauncher;
private ActivityResultLauncher<String> permissionLauncher;
注册启动器
返回值
activityLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), (result) -> {
if (result.getResultCode() == RESULT_BTN1) {//是被启动AC的this.setResult()中设置的结果码
Intent data = result.getData();
if (data != null) {
String resultData = data.getStringExtra("Result1");//是被启动AC的putExtra中的键
tv.setText(resultData);
}
} else if (result.getResultCode() == RESULT_BTN2) {
Intent data = result.getData();
if (data != null) {
int resultInt = data.getIntExtra("Result2", -1);
tv.setText(String.valueOf(resultInt));
}
} else {
tv.setText("没有找到返回值");
}
});
if (result.getResultCode() == RESULT_BTN1) {//是被启动AC的this.setResult()中设置的结果码
Intent data = result.getData();
if (data != null) {
String resultData = data.getStringExtra("Result1");//是被启动AC的putExtra中的键
tv.setText(resultData);
}
} else if (result.getResultCode() == RESULT_BTN2) {
Intent data = result.getData();
if (data != null) {
int resultInt = data.getIntExtra("Result2", -1);
tv.setText(String.valueOf(resultInt));
}
} else {
tv.setText("没有找到返回值");
}
});
权限
permissionLauncher = registerForActivityResult(new ActivityResultContracts.RequestPermission(),
(isGranted) -> {
if (isGranted) {
Toast.makeText(this, "Camera permission granted", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(this, "Camera permission denied", Toast.LENGTH_SHORT).show();
}
});
(isGranted) -> {
if (isGranted) {
Toast.makeText(this, "Camera permission granted", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(this, "Camera permission denied", Toast.LENGTH_SHORT).show();
}
});
被启动Ac.java
sonbtn1.setOnClickListener((v)->{
Intent i = new Intent();
i.putExtra("Result1","你好");
this.setResult(RESULT_BTN1,i);
this.finish();
});
sonbtn2.setOnClickListener((v)->{
Intent i = new Intent();
i.putExtra("Result2",100);
this.setResult(RESULT_BTN2,i);
this.finish();
});
Intent i = new Intent();
i.putExtra("Result1","你好");
this.setResult(RESULT_BTN1,i);
this.finish();
});
sonbtn2.setOnClickListener((v)->{
Intent i = new Intent();
i.putExtra("Result2",100);
this.setResult(RESULT_BTN2,i);
this.finish();
});
发送和接收普通信息(Java)
发送器.java
bt1.setOnClickListener((v)->{
String stu = et1.getText().toString();
Intent i = new Intent("broadcast");
i.setPackage(getPackageName()); // 设置当前应用的包名,使广播成为显式广播
i.putExtra("message",stu);
this.sendBroadcast(i);
});
String stu = et1.getText().toString();
Intent i = new Intent("broadcast");
i.setPackage(getPackageName()); // 设置当前应用的包名,使广播成为显式广播
i.putExtra("message",stu);
this.sendBroadcast(i);
});
优化短时间多次点击
bt1.postDelayed(() -> {isClick = true}, 2000)
接收器.java
静态注册必须有具体类
继承BroadcastReceiver抽象类
继承BroadcastReceiver抽象类
public class Rec extends BroadcastReceiver {
@Override
public void onReceive(Context c , Intent i){
String msg = i.getStringExtra("message");
Toast.makeText(c,msg,Toast.LENGTH_LONG).show();
}
}
@Override
public void onReceive(Context c , Intent i){
String msg = i.getStringExtra("message");
Toast.makeText(c,msg,Toast.LENGTH_LONG).show();
}
}
事件绑定
点击事件
View.OnClickListener接口
实现接口
单事件绑定
实现接口
implements View.OnClickListener
btn.setOnClickListener(this)
@Override
public void onClick(View v){
//Intent i = new Intent(this , IntIMGActivity.class);
Intent i = new Intent();
i.setAction("yinshiqidong");
i.addCategory(Intent.CATEGORY_DEFAULT);
this.startActivity(i);
}
public void onClick(View v){
//Intent i = new Intent(this , IntIMGActivity.class);
Intent i = new Intent();
i.setAction("yinshiqidong");
i.addCategory(Intent.CATEGORY_DEFAULT);
this.startActivity(i);
}
多事件绑定
btn1.setOnClickListener(this);
btn2.setOnClickListener(this);
btn3.setOnClickListener(this);
btn2.setOnClickListener(this);
btn3.setOnClickListener(this);
@Override
public void onClick(View v) {
// 根据按钮ID执行不同的操作
switch (v.getId()) {
case R.id.btn1:
// 第一个按钮的逻辑
Intent i1 = new Intent();
i1.setAction("action_for_button1");
i1.addCategory(Intent.CATEGORY_DEFAULT);
startActivity(i1);
break;
………………
public void onClick(View v) {
// 根据按钮ID执行不同的操作
switch (v.getId()) {
case R.id.btn1:
// 第一个按钮的逻辑
Intent i1 = new Intent();
i1.setAction("action_for_button1");
i1.addCategory(Intent.CATEGORY_DEFAULT);
startActivity(i1);
break;
………………
Lambda语句
因为View.OnClickListener接口只有一个抽象方法OnClick()
btn.setOnClickListener((v)->{
Intent i =new Intent();
});
Intent i =new Intent();
});
匿名内部类
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 按钮1的处理逻辑
Intent i = new Intent();
i.setAction("action_for_button1");
startActivity(i);
}
});
@Override
public void onClick(View v) {
// 按钮1的处理逻辑
Intent i = new Intent();
i.setAction("action_for_button1");
startActivity(i);
}
});
多媒体
音频
Java
使用MediaPlayer
创建对象实例
MediaPlayer mp = creat( Context c , int resource);
MediaActivity.this R.raw.music
MediaActivity.this R.raw.music
播放属性
是否循环
mp.setLooping(boolean)
true(循环)
播放音量
mp.setVolume(left,right)
0.5f , 0.5f
开始
mp.start()
暂停
mp.pause()
关闭
mp.stop();
mp.release();
mp = null;
mp.release();
mp = null;
一定要同时使用,release可以释放占用的资源
视频
使用封装了MediaPlayer的VideoView
XML
VideoView组件
<VideoView
android:id="@+id/video_01"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
android:id="@+id/video_01"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
Java
设置路径
Uri(推荐)
vv.setVideoURI(Uri.parse("android.resource://"+getPackageName()+"/"+R.raw.myvideo));
path(不推荐)
vv.setVideoPath()
设置控制器
MediaController mc = new MediaController(this);//创建控制器实例
mc.setAnchorView(vv);//控制器锚定的组件
vv.setMediaController(mc);//绑定控制器的组件
mc.setAnchorView(vv);//控制器锚定的组件
vv.setMediaController(mc);//绑定控制器的组件
数据库管理(MediaStore API)
核心架构
分层设计
音频
MediaStore.Audio
视频
MediaStore.Video
图片
MediaStore.Image
数据更新
由系统服务MediaScannerService自动更新
核心操作
查询
获取CR对象
ContentResolver resolver = getContentResolver();(隐式通过this(上下文)获取)
执行查询
// 查询所有音乐文件
Cursor cursor = resolver.query(
MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
new String[]{
MediaStore.Audio.Media._ID, // 文件 ID
MediaStore.Audio.Media.TITLE, // 标题
MediaStore.Audio.Media.ARTIST, // 艺术家
MediaStore.Audio.Media.DATA // 文件路径(Android 10+ 受限)
},
null, null, MediaStore.Audio.Media.DEFAULT_SORT_ORDER
);
while (cursor.moveToNext()) {
String title = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.TITLE));
// 其他字段解析...
}
cursor.close(); // 必须关闭游标!
Cursor cursor = resolver.query(
MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
new String[]{
MediaStore.Audio.Media._ID, // 文件 ID
MediaStore.Audio.Media.TITLE, // 标题
MediaStore.Audio.Media.ARTIST, // 艺术家
MediaStore.Audio.Media.DATA // 文件路径(Android 10+ 受限)
},
null, null, MediaStore.Audio.Media.DEFAULT_SORT_ORDER
);
while (cursor.moveToNext()) {
String title = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.TITLE));
// 其他字段解析...
}
cursor.close(); // 必须关闭游标!
增加
新建CV对象
ContentValues values = new ContentValues();
执行添加
values.put(MediaStore.Audio.Media.TITLE, "New Song");
values.put(MediaStore.Audio.Media.DATA, "/sdcard/Music/song.mp3");
Uri newUri = resolver.insert(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, values);
values.put(MediaStore.Audio.Media.DATA, "/sdcard/Music/song.mp3");
Uri newUri = resolver.insert(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, values);
删除
执行删除
resolver.delete(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI , MediaStore.Audio.Media._ID + "=?", new String[]{String.valueOf(id)});
后台任务
UI更新
Android规定,每个Activity都有自己的UI线程(Actuvuty主线程)
对于Activity的UI(显示内容)更新,必须由UI线程执行
对于Activity的UI(显示内容)更新,必须由UI线程执行
android提供了一个信息队列Handler类
(在主线程创建)
(在主线程创建)
功能
用于非UI线程和UI线程的通信
例如下载文件,通过其他线程下载,然后将结果传递给Handler,
Handler再通知UI线程更新显示下载已完成
Handler再通知UI线程更新显示下载已完成
机制
对于简单数据
通过Handler对象h发送Runnable
h.post(r)
封装可执行任务Runnable
Runnable r = new Runnable() {
@Override
public void run() {
if(starts) {
ldt = LocalDateTime.now();
String time = ldt.format(dtf);
back_tv01.setText(time);
h.postDelayed(r,1000);//每隔一秒回调r
}
}
};
@Override
public void run() {
if(starts) {
ldt = LocalDateTime.now();
String time = ldt.format(dtf);
back_tv01.setText(time);
h.postDelayed(r,1000);//每隔一秒回调r
}
}
};
对于复杂数据
开启新线程,并在耗时任务结束后通过Handler发送Runnable
new Thread(()->{
//耗时操作
//耗时任务结束
h.post(new Runnable(){
@Override
public void run() {
}
});
});
//耗时操作
//耗时任务结束
h.post(new Runnable(){
@Override
public void run() {
}
});
});
更简洁的双Lambda语句
new Thread(()->{
//耗时操作
//耗时任务结束
h.post(()->{
});
});
new Thread(()->{
//耗时操作
//耗时任务结束
h.post(()->{
});
});
通过Handler对象h发送Message
h.sendMessage(Message)
回调
h.postDelayed(执行对象,1000);//1秒回调
下载文件/密集运算
通过Thread类
但仍需通过Handler更新UI显示
后台服务
Servicer组件(非UI组件)
运行(在UI线程进行)
启动服务
第一次
startService()
onCreate()初始化
onStartCommand()
非首次
startService()
onStartCommand()
停止服务
被动停止
stopService()
onDestroy()
主动停止
stopSelf()
onDestroy()
特征
生命周期独立于Activity(后台本质)
后台!在启动服务的组件被停止后,Service依旧可以运行(后台播放音乐)
运行于UI线程
Service的onCreate(), onStartCommand()等方法默认在主线程执行
Service是运行在应用程序的UI线程上的,所以只能进行一些简短的工作,
不能有阻塞操作
不能有阻塞操作
建议
在Service的回调方法中显示创建子线程,Service能保证子线程运行直到run()执行完毕,或整个应用程序摧毁
使用
在要启动服务的Activity
Intent service = new Intent(this , MyService.class);
service_btn01.setOnClickListener((v) -> {
service.putExtra("music",R.raw.apex);
this.startService(service);
});
service_btn02.setOnClickListener((v) -> {
this.stopService(service);
});
service_btn01.setOnClickListener((v) -> {
service.putExtra("music",R.raw.apex);
this.startService(service);
});
service_btn02.setOnClickListener((v) -> {
this.stopService(service);
});
在MyService.java
onBind()
// 必须重写但无需实现(非绑定服务)
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
onCreate()
创建前
public void onCreate(){
mp = null;//例如初始化等一些简单行为
}
mp = null;//例如初始化等一些简单行为
}
onStartCommand(Intent service , int flags , int startId)
参数
启动该服务的Activity传递的Intent对象
由系统传入
(大多数情况可忽略)
(大多数情况可忽略)
int flags- 启动标志位
这个参数表示系统启动服务的特殊标志,
它是一个位掩码值(可以组合多个标志)。
这个参数表示系统启动服务的特殊标志,
它是一个位掩码值(可以组合多个标志)。
int startId- 启动ID
这个参数是系统为每次启动请求分配的唯一标识符(从1开始递增)。它的核心作用:
识别不同的启动请求(例如多次调用 startService())
安全停止服务(使用 stopSelf(startId)而不是 stopSelf())
这个参数是系统为每次启动请求分配的唯一标识符(从1开始递增)。它的核心作用:
识别不同的启动请求(例如多次调用 startService())
安全停止服务(使用 stopSelf(startId)而不是 stopSelf())
public int onStartCommand(Intent service , int flags , int startId){
return START_STICKY;
}
return START_STICKY;
}
使用 START_STICKY返回值,系统会自动处理重启和Intent重发,所以无需特别关注
onDestroy()
销毁前
@Override
public void onDestroy() {
if (mp !=null){
if (mp.isPlaying() == true){
mp.stop();
}
mp.release();
}
}
public void onDestroy() {
if (mp !=null){
if (mp.isPlaying() == true){
mp.stop();
}
mp.release();
}
}
权限获取
声明
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<application
</application>
<!-- 相机权限声明 -->
<uses-permission android:name="android.permission.CAMERA" />
<!-- 声明应用可以使用摄像头硬件,但不是必须要有才能运行 -->
<uses-feature android:name="android.hardware.camera" android:required="false" />
</manifest>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<application
</application>
<!-- 相机权限声明 -->
<uses-permission android:name="android.permission.CAMERA" />
<!-- 声明应用可以使用摄像头硬件,但不是必须要有才能运行 -->
<uses-feature android:name="android.hardware.camera" android:required="false" />
</manifest>
获取
homebtn2.setOnClickListener((v) -> {
permissionLauncher.launch(Manifest.permission.CAMERA);
});
permissionLauncher.launch(Manifest.permission.CAMERA);
});
permissionLauncher = registerForActivityResult(
new ActivityResultContracts.RequestPermission(),
(isGranted) -> {
if (isGranted) {
Toast.makeText(this, "Camera permission granted", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(this, "Camera permission denied", Toast.LENGTH_SHORT).show();
}
});
new ActivityResultContracts.RequestPermission(),
(isGranted) -> {
if (isGranted) {
Toast.makeText(this, "Camera permission granted", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(this, "Camera permission denied", Toast.LENGTH_SHORT).show();
}
});
数据管道
存储程序数据
SharedPreferences类
创建对象
SharedPreferences sp = getSharedPreferences("user" ,0);
SharedPreferences sp = getPreferences(0);
简单封装SharedPreferences,将name固定为调用该方法的类名
保存数据
获取编辑器对象
Share的Preferences.Editor e = sp.edit()
以键值对形式添加数据
e.putString("key" , "value")
Int
Long
……
Int
Long
……
保存
e.commit()
获取数据
通过键获取值
String name = sp.getString("name" , " ")
参数可以设置多个,按顺序获取,直到" "
例如第一次name没有对应的值,便获取“ ”
例如第一次name没有对应的值,便获取“ ”
Preference API设置应用首选项
(也就是SharedPreference类)
(也就是SharedPreference类)
XML
首选项资源
res/xml/settings.xml
容器
<PreferenceScreen></PreferenceScreen>
分类框
<PreferenceGateCory
android:title="一个板块的名称,例如文字设置">
组件……
</PreferenceGateCory>
android:title="一个板块的名称,例如文字设置">
组件……
</PreferenceGateCory>
组件
复选框
CheckBoxPreference
列表框
ListPreference
输入框
EditTextPreference
XML配置属性
共有属性
键
android:key
默认值
android:defaultValue
标题
android:title
提示
android:summary
列表框特有
可视选项
android:entries
例如语言的
中文
Englis的可视化选项
中文
Englis的可视化选项
生效选项
android:entryValues
实际改变的提供给程序的选项
对应可视化选项
1
2
对应可视化选项
1
2
属性值引用的资源
res/string.xml
<string-array name="lang">
<item>中文</item>
<item>English</item>
</string-array>
<item>中文</item>
<item>English</item>
</string-array>
Java
MySetting.java
在AndroidManifest.xml注册
public class MySetting extends PreferenceActivity { 继承PreferenceActivity
public static String TEXT_ANIMATION = "text_animation";
public static String LANG_TYPE = "lang_type";
public static String FONT_SIZE = "font_size";
public static String FONT_COLOR = "font_color";
@SuppressWarnings("deprecation")
@Override
public void onCreate(Bundle s){回调onCreate()
super.onCreate(s);
getPreferenceManager().setSharedPreferencesName("set");//保存到自定义名为set的文件中
addPreferencesFromResource(R.xml.my_setting);//添加Preference通道指向布局文件
}
}
public static String TEXT_ANIMATION = "text_animation";
public static String LANG_TYPE = "lang_type";
public static String FONT_SIZE = "font_size";
public static String FONT_COLOR = "font_color";
@SuppressWarnings("deprecation")
@Override
public void onCreate(Bundle s){回调onCreate()
super.onCreate(s);
getPreferenceManager().setSharedPreferencesName("set");//保存到自定义名为set的文件中
addPreferencesFromResource(R.xml.my_setting);//添加Preference通道指向布局文件
}
}
FirstActivity
在AndroidManifest.xml注册
在onCreate()中
获取SharedPreference对象
自定义
SharedPreferences settings = getSharedPreferences("set", MODE_PRIVATE);
需要在设置的java中指定
默认
已标废弃SharedPreference settings = PreferenceManager.getDefaultSharedPreferences(this)
推荐SharedPreferences settings = getSharedPreferences(getPackageName() + "_preferences", MODE_PRIVATE),俩者等效
Lambda设置监听改变方法
Lambda方法引用
settings.registerOnSharedPreferenceChangeListener(this::changeMethod);
public void changeMethod(SharedPreference settings , String key){}
public void changeMethod(SharedPreference settings , String key){}
settings.registerOnSharedPreferenceChangeListener((setting , key)->{
});
});
settings = setting,不同名
在监听器中实现逻辑
当my_settings.xml中,某个组件的默认值改变时,会返回那个组件的key
利用例如if(key.equalsIgnoreCase(MySetting.TEXT_ANIMATION)){
使用settings.getXXX获取对应键的值,通常是if中与key对比的那个(MySetting.TEXT_ANIMATION)
if (key.equalsIgnoreCase(MySetting.TEXT_ANIMATION)) {
Boolean anim = setting.getBoolean(MySetting.TEXT_ANIMATION, false);
if (anim) {
Animation scale = AnimationUtils.loadAnimation(this, R.anim.text_scale);
fr_tv01.startAnimation(scale);
} else if (!anim) {
fr_tv01.clearAnimation(); // 立即停止所有动画[1,2](@ref)
}
} else if…………
使用settings.getXXX获取对应键的值,通常是if中与key对比的那个(MySetting.TEXT_ANIMATION)
if (key.equalsIgnoreCase(MySetting.TEXT_ANIMATION)) {
Boolean anim = setting.getBoolean(MySetting.TEXT_ANIMATION, false);
if (anim) {
Animation scale = AnimationUtils.loadAnimation(this, R.anim.text_scale);
fr_tv01.startAnimation(scale);
} else if (!anim) {
fr_tv01.clearAnimation(); // 立即停止所有动画[1,2](@ref)
}
} else if…………
(在旧安卓上实际没多大意义,因为设置界面的是由系统决定的,
很难更改设置界面的样式,其他界面又因为在后台无法触发监
听器,更偏向于回到界面时调用方法去更新)
很难更改设置界面的样式,其他界面又因为在后台无法触发监
听器,更偏向于回到界面时调用方法去更新)
返回刷新
@Override
protected void onResume() {
super.onResume();
// 关键:每次从后台返回时重新加载设置
loadSettings();
}
private void loadSettings() {
类似监听器逻辑
}
protected void onResume() {
super.onResume();
// 关键:每次从后台返回时重新加载设置
loadSettings();
}
private void loadSettings() {
类似监听器逻辑
}
当主页面activity切到后台时,监听器是不会生效的,所以监听器一般放在设置页面的activity实时更新
实现首选项
每次启动时(会调用onCreate()),使用sp对象获取并设置值
SQLite数据库
网络
网络基础
HTTP/HTTPS协议: 这是安卓与服务器通信的绝对主流方式。
理解请求(GET, POST, PUT, DELETE等)、响应(状态码200, 404, 500等)、请求头、响应头、URL结构、查询参数、请求体(表单、JSON)等基础概念.
理解请求(GET, POST, PUT, DELETE等)、响应(状态码200, 404, 500等)、请求头、响应头、URL结构、查询参数、请求体(表单、JSON)等基础概念.
请求
GET
只读操作,用于获取资源
安全,幂等(执行不管多少次都是相同的结果)
POST
提交数据并创建新资源,会返回响应结果
不安全,不幂等
PUT
全量替换现有资源
不安全,幂等
DELETE
删除指定资源,即使指定资源不存在依旧返回成功
不安全,幂等
响应
2xx 成功
200 OK
请求成功,服务器已返回请求的资源(如网页、API数据)
4xx 客户端错误
404 Not Found
请求的资源在服务器上不存在(如URL错误、资源被删除)
客户端请求了服务器不存在的资源
客户端请求了服务器不存在的资源
5xx 服务器错误
500 Internal Server Error
服务器内部处理请求时发生未预料的错误
Headers类
Headers h = new Headers()
请求头
客户端对服务器的要求清单(客户端发出的元数据)
响应头
服务器对客户端的操作指南(服务器返回的指令)
安卓网络权限
在 AndroidManifest.xml 中添加网络权限:<uses-permission android:name="android.permission.INTERNET" />
管理网络状态
ConnectivityManager
ConnectivityManager cm = (ConnectivityManager) this.getSystemService(Context.CONNECTIVITY_SERVICE);
当前Java文件名
当前Java文件名
常用方法
cm.getActiveNetworkInfo();
获取当前活动的网络信息,没有则返回null
cm.getAllNetworkInfo();
获取系统支持的所有网络信息
接收对象
NetworkInfo(简单获取网络状态)
获取的信息简单,而且无法验证联网状态(真正可上网),有的Wifi要登陆验证等情况
NetworkInfo ni = cm.getActiveNetworkInfo();
cm.getAllNetworkInfo();
cm.getAllNetworkInfo();
ni == null(没有活动网络)
ni.isConnected()
ni.getTypeName()是活动的
NetworkCapabilities·
获取当前活动的网络(可能并不具备真正联网能力)
Network activeNetwork = cm.getActiveNetwork();
if (activeNetwork == null) {
net_tv01.setText("当前没有活动网络");
return;
}
if (activeNetwork == null) {
net_tv01.setText("当前没有活动网络");
return;
}
当有活动的网路时,尝试获取活动网络的信息
获取活动网络的信息
NetworkCapabilities nc = cm.getNetworkCapabilities(activeNetwork);
if (nc == null) {
net_tv01.setText("网络能力不可用");
return;
}
如果获取到的能力对象为null,说明虽然系统有一个活动网络,
但我们无法获取到它的能力信息(这种情况很少见,但安全起见需要处理),
则显示“网络能力不可用”并返回。
NetworkCapabilities nc = cm.getNetworkCapabilities(activeNetwork);
if (nc == null) {
net_tv01.setText("网络能力不可用");
return;
}
如果获取到的能力对象为null,说明虽然系统有一个活动网络,
但我们无法获取到它的能力信息(这种情况很少见,但安全起见需要处理),
则显示“网络能力不可用”并返回。
有活动的网络,并且成功获取信息后
// 检查网络是否有效连接 表示该网络有能力访问互联网(但不一定已经验证通过)internet
boolean isConnected = nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) &&
nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED);
validated表示该网络已经通过验证,即真正可以访问互联网
boolean isConnected = nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) &&
nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED);
validated表示该网络已经通过验证,即真正可以访问互联网
检查类型
// 获取网络类型名称
String networkType = "未知网络"; transport
if (nc.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) {
networkType = "WiFi"; cellular
} else if (nc.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) {
networkType = "蜂窝数据"; ethernet
} else if (nc.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET)) {
networkType = "以太网";
} else if (nc.hasTransport(NetworkCapabilities.TRANSPORT_VPN)) {
networkType = "VPN";
}
// 设置文本显示
if (isConnected) {
net_tv01.setText(networkType + "是活动的");
} else {
net_tv01.setText(networkType + "不在服务区");
}
String networkType = "未知网络"; transport
if (nc.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) {
networkType = "WiFi"; cellular
} else if (nc.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) {
networkType = "蜂窝数据"; ethernet
} else if (nc.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET)) {
networkType = "以太网";
} else if (nc.hasTransport(NetworkCapabilities.TRANSPORT_VPN)) {
networkType = "VPN";
}
// 设置文本显示
if (isConnected) {
net_tv01.setText(networkType + "是活动的");
} else {
net_tv01.setText(networkType + "不在服务区");
}
执行网络请求
主线程限制
Android严禁在主线程(UI线程)执行网络操作,否则会抛出 NetworkOnMainThreadException。所有网络请求都必须是异步的!
核心工具/库
HttpURLConnection (原生)
共有操作
设置目标URL
URL url = new URL("https://jsonplaceholder.typicode.com/XXXX");
俩个方法的后面会有些许不同
通过URL获取连接对象
HttpsURLConnection huc = (HttpsURLConnection) url.openConnection();
为连接对象设置超时
huc.setConnectTimeout(10000); // 连接超时10秒
huc.setReadTimeout(10000); // 读取超时10秒
huc.setReadTimeout(10000); // 读取超时10秒
获取响应码
int responseCode = huc.getResponseCode();
读取输入流
if (responseCode == ??) {
// 读取输入流
InputStream inputStream = huc.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
StringBuilder response = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
response.append(line);
}
// 关闭流
reader.close();
inputStream.close();
// 读取输入流
InputStream inputStream = huc.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
StringBuilder response = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
response.append(line);
}
// 关闭流
reader.close();
inputStream.close();
GET方法获取数据
设置URL
URL url = new URL("https://jsonplaceholder.typicode.com/todos/1");
设置请求方法为GET
huc.setRequestMethod("GET");//请求方式设为GET
进行连接
huc.connect();
判断响应码
if (responseCode == HttpURLConnection.HTTP_OK) {
// 读取输入流
}
// 读取输入流
}
200(OK)代表成功
代码
try {
URL url = new URL("https://jsonplaceholder.typicode.com/todos/1");
HttpsURLConnection huc = (HttpsURLConnection) url.openConnection();
huc.setRequestMethod("GET");
huc.setConnectTimeout(10000); // 连接超时10秒
huc.setReadTimeout(10000); // 读取超时10秒
huc.connect();
// 获取响应码
int responseCode = huc.getResponseCode();
if (responseCode == HttpURLConnection.HTTP_OK) {
// 读取输入流
InputStream inputStream = huc.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
StringBuilder response = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
response.append(line);
}
// 关闭流
reader.close();
inputStream.close();
// 得到JSON字符串
String jsonResponse = response.toString();
// 解析JSON
JSONObject todo = new JSONObject(jsonResponse);
userId = todo.getInt("userId");
id = todo.getInt("id");
title = todo.getString("title");
completed = todo.getBoolean("completed");
}
} catch (Exception e) {
// 捕获所有异常,包括超时和网络不可用
Log.e("NETWORK", "请求异常", e);
return "请求失败: " + e.getMessage();
}
return "用户ID:" + userId + "\n 代办事项标识符:" + id + "\n标题: " + title + "\n状态:" + (completed ? "完成" : "未完成");
}
URL url = new URL("https://jsonplaceholder.typicode.com/todos/1");
HttpsURLConnection huc = (HttpsURLConnection) url.openConnection();
huc.setRequestMethod("GET");
huc.setConnectTimeout(10000); // 连接超时10秒
huc.setReadTimeout(10000); // 读取超时10秒
huc.connect();
// 获取响应码
int responseCode = huc.getResponseCode();
if (responseCode == HttpURLConnection.HTTP_OK) {
// 读取输入流
InputStream inputStream = huc.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
StringBuilder response = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
response.append(line);
}
// 关闭流
reader.close();
inputStream.close();
// 得到JSON字符串
String jsonResponse = response.toString();
// 解析JSON
JSONObject todo = new JSONObject(jsonResponse);
userId = todo.getInt("userId");
id = todo.getInt("id");
title = todo.getString("title");
completed = todo.getBoolean("completed");
}
} catch (Exception e) {
// 捕获所有异常,包括超时和网络不可用
Log.e("NETWORK", "请求异常", e);
return "请求失败: " + e.getMessage();
}
return "用户ID:" + userId + "\n 代办事项标识符:" + id + "\n标题: " + title + "\n状态:" + (completed ? "完成" : "未完成");
}
POST方法发送数据
设置URL
URL url = new URL("https://jsonplaceholder.typicode.com/posts");
设置请求方法为POST
huc.setRequestMethod("POST");
启用输出流(允许写入请求体)
huc.setDoOutput(true);
写入请求体数据
(getOutpotStream隐式包含连接请求)
(getOutpotStream隐式包含连接请求)
// 示例:写入JSON数据
String postData = "{\"title\":\"foo\",\"body\":\"bar\",\"userId\":1}";//发送到服务端的数据
try (OutputStream os = huc.getOutputStream()) {
byte[] input = postData.getBytes(StandardCharsets.UTF_8);
os.write(input, 0, input.length);
}
String postData = "{\"title\":\"foo\",\"body\":\"bar\",\"userId\":1}";//发送到服务端的数据
try (OutputStream os = huc.getOutputStream()) {
byte[] input = postData.getBytes(StandardCharsets.UTF_8);
os.write(input, 0, input.length);
}
设置内容类型
(告诉服务器数据格式)
(告诉服务器数据格式)
huc.setRequestProperty("Content-Type", "application/json; charset=UTF-8");
判断响应码
if (responseCode == HttpURLConnection.HTTP_CREATED) {
// ... 读取响应流
}
// ... 读取响应流
}
201(created)代表成功
服务器处理完POST请求后,必须返回处理结果(如操作成功提示、生成的新数据、错误信息等)。
这个结果通过响应体(Response Body)返回,客户端需通过输入流(InputStream)读取
这个结果通过响应体(Response Body)返回,客户端需通过输入流(InputStream)读取
OkHttp(业界标准 - 必学)
在build.gradle.kts中添加依赖
dependencies {
implementation("com.squareup.okhttp3:okhttp:4.9.2")
}
implementation("com.squareup.okhttp3:okhttp:4.9.2")
}
Java
引入
import okhttp3.*;
创建客户端
OkHttpClient client = new OkHttpClient();
构建请求
Request request = new Request.Builder()
.url("https://jsonplaceholder.typicode.com/posts/1")
.build();
.url("https://jsonplaceholder.typicode.com/posts/1")
.build();
发起异步请求
并实现方法
并实现方法
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(@NonNull Call call, @NonNull IOException e) {
}
@Override
public void onResponse(@NonNull Call call, @NonNull Response response) {
try {
String result = response.body().string();
runOnUiThread(() -> {
TextView tv = findViewById(R.id.ok_tv01);
tv.setText(result);
});
} catch (IOException e) {
}
}
});
@Override
public void onFailure(@NonNull Call call, @NonNull IOException e) {
}
@Override
public void onResponse(@NonNull Call call, @NonNull Response response) {
try {
String result = response.body().string();
runOnUiThread(() -> {
TextView tv = findViewById(R.id.ok_tv01);
tv.setText(result);
});
} catch (IOException e) {
}
}
});
获取解析JSON
// 得到JSON字符串
String jsonResponse = response.toString();
// 解析JSON
JSONObject todo = new JSONObject(jsonResponse);
int userId = todo.getInt("userId");
int id = todo.getInt("id");
String title = todo.getString("title");
boolean completed = todo.getBoolean("completed");
String jsonResponse = response.toString();
// 解析JSON
JSONObject todo = new JSONObject(jsonResponse);
int userId = todo.getInt("userId");
int id = todo.getInt("id");
String title = todo.getString("title");
boolean completed = todo.getBoolean("completed");
与HTML5混合开发
本地网页文件
项目—app—src—main—assets
html
css
js
assets与java,res为同级文件夹
XML
WebView组件
Java
获取组件对象
WebView web01 = findViewById(R.id.web01)
设置网页组件客户端
web01.setWebViewClient(new WebViewClient());
设置Html网页
在线网页
web01.loadUrl("https://m.baidu.com");
明前明文禁止使用HTTP推荐使用HTTPS
需要在AndroidManifest.xml声明网络权限
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.INTERNET"/>
本地网页
web01.loadUrl("file:///android_asset/html/Stu.html");
不是assets
设置JavaScript可行
WebSettings webSettings = web01.getSettings();
webSettings.setJavaScriptEnabled(true);
webSettings.setJavaScriptEnabled(true);
HTML与Android的信息交互
Activity.java
通过创建HTMLSupport对象,调用HTMLSupport中的方法,将用户输入或改变的数据传递给HTMLSupport
HTMLSupport htmls = new HTMLSupport(this);
往WebView注入javadHTMLSupport对象
web01(WebView绑定的对象).addJavascriptInterface(htmls , "android")
android为指定JS中java对象的属性名
在html的js中(
使用window.android即可调用有@JavascriptInterace注解的方法
window.android.getInfo_android_to_html();
window.android.startNewActivity(str);
)
在html的js中(
使用window.android即可调用有@JavascriptInterace注解的方法
window.android.getInfo_android_to_html();
window.android.startNewActivity(str);
)
HTMLSupport.java
作为Android与HTML交互的桥梁,从Activity,java获取数据,将数据通过方法提供给HTML获取
必备
初始量
Context mContext;
String info_android_to_html;
String info_html_to_android;
String info_android_to_html;
String info_html_to_android;
构造方法
public HTMLSupport(Context mContext){
this.mContext = mContext;
}
this.mContext = mContext;
}
必须获取Activity.java的上下文
推荐设置的方法
允许JS读取
@JavascriptInterface
@JavascriptInterface
@JavascriptInterface是 Android WebView 开发中用于标记 Java 方法可被 JavaScript 安全调用的核心注解
通过 addJavascriptInterface()将 Java 对象注入 WebView 后,只有添加了 @JavascriptInterface注解的公共方法才能被 JavaScript 调用
JavaScript 调用的 Java 方法默认运行在 WebView 的 UI 线程,避免直接执行耗时操作
通过 addJavascriptInterface()将 Java 对象注入 WebView 后,只有添加了 @JavascriptInterface注解的公共方法才能被 JavaScript 调用
JavaScript 调用的 Java 方法默认运行在 WebView 的 UI 线程,避免直接执行耗时操作
获取 Android → HTML 传递的数据
public String getInfo_android_to_html(){
return info_android_to_html;
}
return info_android_to_html;
}
设置 HTML → Android 的数据
public void setInfo_html_to_android(String info_html_to_android){
this.info_html_to_android = info_html_to_android;
}
this.info_html_to_android = info_html_to_android;
}
开启新Activity
(发送HTML→ Android 的数据)
(发送HTML→ Android 的数据)
public void startNewActivity(String message){
Intent i = new Intent(mContext , 目标AC.clss)
i.putExtra("message" , message);
mContext.startActivity(i);
}
Intent i = new Intent(mContext , 目标AC.clss)
i.putExtra("message" , message);
mContext.startActivity(i);
}
其他
设置Android → HTML 传递的数据
public void setInfo_android_to_html(String info_android_to_html){
this.info_android_to_html = info_android_to_html;
}
this.info_android_to_html = info_android_to_html;
}
获取HTML → Android 的数据
public String getInfo_html_to_android(){
return info_html_to_android;
}
return info_html_to_android;
}
HTML
在Activity.java中利用addJavascriptInterface()将 Java 对象注入 WebView
web01(WebView绑定的对象).addJavascriptInterface(htmls , "android")
android为指定JS中java对象的属性名
在html的js中(
使用window.android即可调用有@JavascriptInterace注解的方法
window.android.getInfo_android_to_html();
window.android.startNewActivity(str);
)
在html的js中(
使用window.android即可调用有@JavascriptInterace注解的方法
window.android.getInfo_android_to_html();
window.android.startNewActivity(str);
)
进阶安卓
主题
自动切换
资源路径
日间模式:res/values/themes.xml
夜间模式:res/values-night/themes.xml
夜间模式:res/values-night/themes.xml
基础窗口属性
整个Activity的默认背景
android:windowBackground
Activity 根布局
全屏功能
<item name="android:windowFullscreen">true</item>
系统状态栏隐藏,下来显示,内容也不会进入状态栏
优化隐藏应用标题栏(最常用)
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
<item name="windowNoTitle">true</item>
单纯取消Title会出现ActionBar
底部虚拟导航栏颜色
android:navigationBarColor
系统导航栏
顶部状态栏颜色
android:statusBarColor
系统状态栏
颜色
应用主色调
colorPrimary
主色调暗色版
colorPrimaryDark
强调色
colorAccent
控件默认颜色
colorControlNormal
控件激活状态颜色
colorControlActivated
主要文字颜色
android:textColorPrimary
次要文字颜色
android:textColorSecondary
字体
全局文本样式
android:textAppearance="@style/TextAppearance.App.Body"
封装了多个文本相关属性的集合,本质是一个样式引用
全局字体
fontFamily
@font/your_font_file <!-- 对应res/font/目录下的字体文件 -->
控件(名+Style)
按钮默认样式
buttonStyle

收藏

收藏
0 条评论
下一页