笔记及代码获取
Client和Server:客户端和服务器端,如QQ、LOL等
Browser和Server:浏览器和服务器端,如京东、淘宝等
java程序开发工具包,包含JRE和开发人员使用的工具。其中开发工具:编译工具(javac.exe)和运行工具(java.exe)
命令行
操作 | 说明 |
---|---|
盘符名称 | 盘符切换。eg:E:回车,表示切换到E盘 |
dir | 查看当前路径下的内容 |
cd目录 | 进入单级目录。eg:cd xxx |
cd… | 回退上一级目录 |
cd 目录1\目录2… | 进入多级目录。eg:cd xxx\xxxx |
cd\ | 回退到盘符目录 |
cks | 清屏 |
exit | 退出命令提示符窗口 |
https://notepad-plus-plus.org/
在程序运行过程中,其值不可以发生改变的量,除了空常量其余的都可以在控制台直接输出
常量类型 | 说明 |
---|---|
字符串常量 | 用双引号括起来的内容 |
整数常量 | 不带小数的数字 |
小数常量 | 带小数的数字 |
字符常量 | 用单引号括起来的内容 |
布尔常量 | 布尔值,表示真假 |
空常量 | 空值 |
整数类型 | 关键字 | 内存占用 | 取值范围 |
整数 | byte | 1 | -128~127 |
short | 2 | -32768~32767 | |
int(默认) | 4 | -2的31次方~2的31次方-1 | |
long | 8 | -2的63次方~2的63次方-1 | |
浮点数 | float | 4 | 负数:-3.402823E+38~-1.401298E-45 正数:4.9000000E-324~1.797693E+308 |
double | 8 | 负数:-1.797693E+308~4.9000000E-324 正数:4.9000000E-324~1.797693E+308 | |
字符 | char | 2 | 0~65535 |
布尔 | boolean | 1 | true、false |
常见命名的约定
类型转换
+ - * / %
‘A’ 对应65 A-Z是连续的
‘a’ 对应97 a-z是连续的
‘0’ 对应45 0-9是连续的
算术表达式中包含多个基本类型数据的时候,整个算术表达式的类型会自动提升,规则:byte、short、char类型会被提升到int类型;整个表达式的类型自动提升到表达式中最高等级操作数同样的类型,等级顺序(低到高):byte→short→char→int→long→float→double。
= += -= *= /= %= …
++ – …
i++:先赋值再自增
++i:先自增再赋值
== != > < >= <=
运算结果为True或者False
Scanner使用步骤:
import java.util.Scanner;//导包
Scanner sc = new Scanner(System.in);//创建对象
int i = sc.nextInt(); //接收数据
/*
输入一个或多个数据时,只需要写一个Scanner sc = new Scanner(System.in);
*/
int i1 = sc.nextInt();
int i2 = sc.nextInt();
int i3 = sc.nextInt();
switch(表达式){
case值1: //后面跟的是要和表达式进行比较的值
语句体1;
break;
…
default: //表示所有情况都不匹配的时候,执行该处内容
语句体n+1;
[break;]
}
注意:在switch语句中,如果case控制的语句体后面不写break,将出现穿透现象,在不判断下一个case值的情况下,向下运行,直到遇到break或者整体switch语句结束
水仙花数:水仙花数是一个三位数,个位、十位、百位的数字立方和等于原数
任意数字的指定位上的数值求法:先使用整除操作将要求的数字移动到个位上,再用取余方法取出最后一位上的值。eg:求X位,用数字整除X,然后将得到的数字用10取余,即可得到指定位上的数值
跳过某次循环体内容的执行,继续下一次的执行:continue
注意:使用是基于条件控制的
终止循环体内容的执行,结束当前的整个循环:break
注意:使用是基于条件控制的
import java.util.Random; //导包
Random r = new Random(); //创建对象
int object = r.nextInt(x); //获取随机数
/*
获取数据的范围:[0,x) 包括0,不包括x
获取1-x之间的随机数:int number = r.nextInt(x) + 1; 注意有+
*/
项目project–>模块1、模块2…–>包1、包2…–>类…
构造方法 : 构建对象的方法叫构造方法;成员方法: 封装了特定功能的代码块。构造方法是为了创建一个类的对象,成员方法是封装了特定功能的代码块
//构造方法的格式
public 类名(形式参数列表){
方法体;
} 注意没有void或者返回类型
//成员方法格式
public 返回值类型 方法名(形式参数列表){
//方法体;(特定功能的代码)
return 返回值;
}
public static viod 方法名(){
方法体
}
//调用
方法名();
//在main()中调用
public static viod 方法名(数据类型 变量名){
方法体
}
//调用
方法名(变量名/常量值);
//在main()中调用
public static 数据类型 方法名(数据类型 变量名){
return 数据;
}
//数据类型必须与return中的数据类型匹配
//调用:
数据类型 变量名 = 方法名(变量名/常量值);
//在main()中调用
子类中出现了与父类中一模一样的方法声明,当子类需要父类的功能,而功能主体子类有自己特有内容时,可以重写父类中的方法,这样即沿袭了父类的功能,有定义了子类特有的内容
格式
System.out.println("内容"); //输出内容并换行
System.out.print("内容");// 输出内容不换行
创建对象:格式:类名 对象名 = new 类名()
使用对象
作用是创建对象
public class 类名{
修饰符 类名(参数){
}
}
功能:完成对象数据的初始化
无参数的构造方法就相当于在B类中重新定义A类中的成员变量的值
有参数的构造方法就相当于在B类中使用此方法时顺带将想要给A类参数赋的值赋值
public class 子类名 extends 父类名{
}
父类也称基类、超类
子类也称派生类
去完成某个行为,当不同对象去完成时会产生出不同的状态
多态的形式:具体类多态、抽象类多态、接口多态
eg:猫
可以说猫是猫:猫 cat = new 猫()
可以说猫是动物:动物 animal = new 猫()
这里猫在不同的时刻表现出了不同的形态,这就是多态
成员变量:编译看左边,执行看左边
成员方法:编译看左边,执行看右边
eg
Animal cat = new Cat()
cat.object 若object在Animal中有并且在Cat中也有,则输出的是Animal中的值;若Animal中没有但在Cat中有,则无法输出
cat.object() 若object在Animal中有并且在Cat中也有,则输出的是Animal中的方法,但如果在Cat中重写了此方法,则输出Cat中重写的此方法;若Animal中没有但在Cat中有,则无法输出
向上转型:从子到父,父类引用指向子类对象
向下转型:从父到子,父类引用转为子类对象
格式
一个没有方法体的方法应该定义为抽象方法,而类中如果有抽象方法,该类必须定义为抽象类
public abstract class 类名{
public abstract void 方法名();
}
注意:抽象方法必须在抽象类里面定义,且抽象方法没有具体的方法体,即不需要写{…}
成员区别
抽象类:变量、常量、构造方法、抽象方法、非抽象方法
接口:常量、抽象方法
关系区别
类与类:继承、单继承
类与接口:实现、单实现、多实现
接口与接口:继承、单继承、多继承
设计理念区别
抽象类:对类抽象,包括属性、行为
接口:对行为抽象
StringBuilder是一个可变的字符串类,可以看做是一个容器,容器里面的内容可变。
与String的区别:String的内容不可变,StringBuilder的内容可以变
//构造方法
StringBuilder Object = new StringBuilder() //无参构造
StringBuilder Object = new StringBuilder(“字符串内容”) //带参构造
//添加方法
Object.append(“添加的内容”) //可以连续链式添加,返回的是对象本身
//反转内容
Object.reverse() //返回的是对象本身
String object = Object.toString()方法//StringBuilder转换为String
StringBuilder Object = new StringBuilder(object)//String转换为StringBuilder
object.add(E e)//添加元素
object.remove(Object o)//删除元素o
object.clear()//清空集合中的元素
object.contains(Object o)//判断集合中是否有指定元素
object.isEmpty()//判断集合是否为空
object.size()//集合长度
//Iterator 迭代器。集合专用遍历方式
Collection<E> object = new ArrayList<E>();
Iterator<E> it = object.iterator();//返回此集合中元素的迭代器,通过集合的iterator()方法得到,这里的E与集合设置中的E是一个类型
//Iterator中常用的方法
Object.next()//返回迭代中的下一个元素
Object.hasNext()//如果迭代具有更多元素,则返回True
//创建集合对象
Collection<E> object = new ArrayList/LinkedList/HashSet/TreeSet <E>();
//添加元素
object.add(“元素”)
//遍历集合
//通过集合对象获取迭代器对象
Iterator<E> it = object.iterator();
//判断是否还有元素
it.hasNext()
//若还有元素,获取下一个元素
it.next()
有序集合,可以精确的空值列别中的每个元素的插入位置,可以通过整数索引来访问元素,列表元素允许重复元素存在。
特点:存储和读取的元素顺序一致;存储的元素可重复
List<E> list = new ArrayList<E>();
//遍历:方法1:
Iterator it = list.iterator();it.hasNext();it.next();方法2:for循环
//添加:
it.add(int index, Object) //指定位置插入元素
//删除:
it.remove(int index) //删除指定位置元素,并返回指定位置元素
//修改元素:
it.set(int index, Object)
//获取元素:
it.get(int index)
注意:当在遍历过程中遇到某个元素时需要添加某个元素时,只能用for循环,不可以用Iterator迭代器遍历。
常用方法
List<E> list = new List<E>()
list.hasNext(); //判断下一个位置知否存在元素
list.hasPrevious(); //判断上一个位置知否存在元素
list. next(); //得到下一个位置的元素
list. previous(); //得到上个位置的元素
list.add(object); //在遍历时可以插入指定元素
简化数组和Collection集合的遍历
for(元素数据类型 变量名:数组或者Collection集合){
//在此处使用变量即可,该变量就是元素
}
//ArrayList构造方法:
ArrayList<引用类型> object = new ArrayList<>()
//ArrayList添加方法:
//添加到目标对象末尾:
object.add(添加的内容)//返回本身,在输出中返回true or false
//添加到目标对象任意位置:
object.add(index, 添加的内容)//返回本身,在输出中返回true or false
//ArrayList删除元素:
//删除目标对象指定元素:
object.remove(x)//返回本身,在输出中返回true or false
//删除指定索引处的元素:
object.remove(index)//返回本身,在输出中返回被删除的元素
//ArrayList修改元素:
//修改指定索引处的元素:
object.set(index,修改的内容)//返回本身,在输出中返回被修改的元素
//ArrayList返回元素:
//返回指定索引处的元素:
object.get(index)//在输出中返回指定索引处的元素
//返回集合中元素的个数:
object.size()//在输出中返回集合元素个数
//LinkedList构造方法:
LinkedList<引用类型> object = new LinkedList<>()
//LinkedList添加方法:
//添加到目标对象开头:
object.addFirst(添加的内容) //在输出中返回true or false
//添加到目标对象末尾:
object.addLast(index, 添加的内容) //在输出中返回true or false
//LinkedList获取元素:
//获取目标对象第一个元素:
object.getFirst()//返回本身,在输出中返回列表中第一个元素
//获取目标对象最后一个元素:
object.getLast()//返回本身,在输出中返回列表中最后一个元素
//LinkedList删除元素:
//删除目标对象第一个元素:
object.removeFirst()//返回本身,在输出中返回第一个元素
//删除目标对象最后一个元素:
object.removeLast ()//返回本身,在输出中返回最后一个元素
集合里的元素顺序不一定与输入元素顺序保持一致
Set<E> set = new HashSet<E>();
注意:利用HashSet存储对象时,要在类中重写hashCode()和equals(),自动生成即可
是JDK根据对象的地址或者字符串或者数字算出来的int类型的数值
public int hashCode():返回对象的哈希值;object.hashCode()//Object类中获取对象哈希值的方法:
注意:object可以是类对象,也可以是具体数据对象。同一个对象多次调用哈希值返回的值是一样的;默认情况下不同对象返回的哈希值是不一样的,当重写hashCode方法时,返回的哈希值是可以相同的。
jdk8之前,底层用数组+链表实现,可以说是一个元素为链表的数组
哈希表默认大小为16的数组,即0-15的数组,然后对存储数据获取哈希值,用此哈希值对16取余,获得的值即为存储在哈希表数组的地址,哈希值不同,取余相同的采取链表存储,哈希值相同,取余相同的咋不采取任何操作
使用的是TreeSet的无参构造方法,格式
TreeSet<E> ts = new TreeSet<E>();
在TreeSet集合中插入元素,想要按元素的某一个属性排序,则需要在类中实现Comparable接口,然后重写comparableT o(E o)方法,其中o.属性表示现有的TreeSet集合中的所有元素,this.属性表示插入的元素,返回两者相减的值,若值大于0则按自然排序插入,若值等于0,则不插入,若值小于0,则按自然排序相反的顺序插入。注意:也可以同时比较多个属性。
//使用的是TreeSet的带参构造方法,格式
TreeSet<Student> ts = new TreeSet<>(new Comparator<Student>() {
@Override
public int compare(E o1, E o2) {
return 0;
}
});
修改方法体中返回值的内容即可,注意o1表示想要插入的元素,o2表示当前TreeSet中所有的元素,想要插入的元素属性比TreeSet集合中的元素属性大,则按从小到大插入,相反…
<类型>//指定一种类型的格式,这里的类型可以看成是形参
<类型1,类型2…>//指定多种类型的格式,多种类型之间用逗号隔开,这里的类型可以看成形参
//将来具体调用时候给定的类型可以看成是实参,并且实参的类型只能是引用类型
修饰符 class 类名<类型>{
} //常见的类型(T、E、K、V)等形式的参数常用与表示泛型,在创建对象的时候,再给一个具体的类型即可
修饰符 <类型> 返回值类型 方法名(类型 变量名){
}
修饰符 interface 接口名 <类型>{
}
为了表示各种泛型List的父类,可以使用类型通配符
修饰符 返回值类型 方法名(数据类型 变量名a,数据类型… 变量名b){
}
注意一般在调用时,在可变参数前的参数是不可变参数,可以有多个,而可变参数b是存储在一个数组中
Arrays中的一个静态方法:
public static <T> List <T> asList(T..a)//返回由指定数组支持的固定大小的列表
注意:返回的集合不能做增删操作,但可以做修改操作
List接口中有一个静态方法:
public static <T> List <T> of(E..elements)//返回包含任意数量元素的不可变列表
注意:返回的集合不能做增删改操作
Set接口中有一个静态方法:
public static <T> Set<T> of(E..elements)//返回一个包含任意数量元素的不可变集合
注意:给的元素不能重复,返回的集合不能做增删操作,没有修改操作
Map<数据类型,数据类型> Obejct = newHashMap<数据类型,数据类型> ()//一般这里的MAP有HashMap(对键没有排序功能)、TreeMap(对键有自然排序的功能)
//添加:
Object.put(键;值)//注意当一个键被重复使用是,即为修改的意思,结果为最后修改的值
//根据键获取值:
Object.get(键)
//获取所有键的集合:
Set<数据类型> object = Obejct.keySet()
//获取所有值的集合:
Collection<数据类型> object = map.values();
//获取所有键值对对象的集合:
Set<Map.Entry<数据类型, 数据类型>> object = Obejct.entrySet();
//删除:
Object.remove(键)
//清除所有键值对元素:
Object.clear
//判断集合是否包含指定的键:
Object.containsKey(Object key)
//判断集合是否包含指定的值:
Object.containsValue(Object Value)
//判断集合是否为空:
Object.isEmpty()
//集合的长度:
Object.size()
public static <T extends Comparable<? super T>> void sort(List<T> list)//将指定的列表按升序排序;Collections.sort(object),返回对象本身
public static void reverse(List<?> list)//反转指定列表中元素的顺序;Collections. reverse (object) ,返回对象本身
public static void shuffle(List<?> list)//使用默认的随机源随机排序指定的列表;Collections. shuffle (object) ,返回对象本身
private、默认、protected、public
修饰符 | 同一个类中 | 同一个包中子类无关类 | 不同包的子类 | 不同包的无关类 |
---|---|---|---|---|
private | true | false | false | false |
默认 | true | true | false | false |
protected | true | true | true | false |
public | true | true | true | true |
可以修饰成员方法、成员变量、类
特点
可以修饰成员方法、成员变量
特点
在一个类中定义一个类,即在A类中定义一个B类,则B类为内部类
public class 类名A{
修饰符 class 类名B{
}
}
分类
在类的成员位置:成员内部类
在类的局部位置(在类的方法里面定义):局部内部类
前提:存在一个类或者接口,类可以使具体类也可以是抽象类
本质是一个继承了该类或者实现了该接口的子类匿名对象
new 类名或者接口名{
重写方法
}.重写的方法名;
//或者
类名或者接口名 object = new new 类名或者接口名{
重写方法
};
object.重写的方法名;
方法名 | 说明 |
---|---|
public static int abs(int a) | 返回参数的绝对值 |
public static double ceil(double a) | 返回大于或等于参数的最小double值,等于一个整数 |
public static doublefloor(double a) | 返回小于或等于参数的最小double值,等于一个整数 |
public static int round(float a) | 返回四舍五入后最接近参数的int值 |
public static int max(int a, int b) | 返回两个int值中的较大值 |
public static int min(int a, int b) | 返回两个int值中的较小值 |
public static double pow(double a, double b) | 返回a的b次幂的值 |
public static double random() | 返回值为double类型的正值 |
System的常用方法,不能被实例化,只能使用类名进行访问
System.exit(0)//在此代码后的所有代码都不运行
System.currentTimeMillis()//输出表示当前时间与1970年1月1日之间的时间差异,以毫秒为单位
//注意:
// 计算一段代码的运行时间
long start_time = System.currentTimeMillis();
for (int i = 0; i < 10000; i++) {
System.out.println(i);
}
long end_time = System.currentTimeMillis();
System.out.println("共耗时:" + (end_time - start_time) + "毫秒");
方法名 | 说明 |
---|---|
public static void exit(int status) | 终止当前运行的java虚拟机,非零表示异常终止 |
public static long currentTimeMillis() | 返回当前时间(以毫秒为单位) |
每个类都将Object作为超类
object. toString()//返回对象的字符串表示形式,一般在类中重写一下tostring方法,和get/set方法一样
equals()方法//object1.equals(object2) 比较两个对象内容是否相同,一般在类中重写一下equals方法,和get/set方法一样
方法名 | 说明 |
---|---|
public String toString() | 返回对象的字符串表示形式,建议所有子类自动生成重写该方法 |
public boolean equals(Object obj) | 比较对象是否相等,默认比较地址,自动生成重写可以比较内容 |
用于操作数组的各种方法
Arrays.sort(Object)//默认从小到大排序,返回对象本身
说明 | 方法名 |
---|---|
返回指定数组的内容的字符串表示形式 | public static String toString(int[] a) |
按照数字顺序排序指定的指数 | public static void sort(int[] a) |
基本数据类型 | 包装类 |
---|---|
byte | Byte |
short | Short |
int | Integer |
long | Long |
float | Float |
double | Double |
char | Character |
boolean | Boolean |
public static final int MIN_VALUE//持有最小值的常数为 `int`可以为-2 31 。
public static final int MAX_VALUE//持有最大值的常数为 `int`可以为2 31 -1。
方法名 | 说明 |
---|---|
public Integer(int value) | 根据int值创建Integer对象 |
public Integer(String s) | 根据String值创建integer对象 |
public static Integer valueOf(int i) | 返回表示指定的int值的Integer实例 |
public static Integer valueOf(String s) | 返回一个保存指定值的Integer对象String |
//int 转换为 String :
public static String ValueOf(int i )//返回int参数的字符串表示形式,该方法是String类中的方法,直接String.valueOf(object)调用
//String 转换为int :
public static String parseInt(String s )//将字符串解析为int类型,该方法是Integer类中的方法直接Integer.valueOf(object)调用
装箱:把基本数据类型转换为对象的包装类类型
拆箱:把包装类类型转换为对应的基本数据类型
//eg:
Integer i = 100; //自动装箱
i += 100; //i = i + 100 : i + 100是自动拆箱;i = i +100是自动装箱
注意:在使用包装类型的时候,如果做操作,最好先判断是否为null,只要是对象,在使用前就必须进行不为null的判断。
方法名 | 说明 |
---|---|
public Date() | 分配一个Date对象,并初始化,代表它被分配的时间,精确到毫秒 |
public Date(long date) | 分配一个Date对象,并将其初始化为表示从标准基准时间起指定的毫秒数 |
方法名 | 说明 |
---|---|
public long getTime() | 获取的是日期对象从1970年1月1日00:00:00到现在的毫秒值 |
public void setTime(long time) | 设置时间,给的是毫秒值 |
一个具体的类,用于以区域设置敏感的方式格式化和解析日期
常用的模式字母及对应关系如下:
y:年、M:月、d:日、H:时、m:分、s:秒
注意大写的M表示月,小写的m表示分
//格式化(从Date到String):
public final String format(Date date)//将日期格式化成日期/时间字符串
//格式:
SimpleDateFormat sdf1 = new SimpleDateFormat();
//无参构造方法输出的结果2022/6/7 下午1:44
SimpleDateFormat sdf2 = new SimpleDateFormat("yyyy年MM月dd日 hh:mm:ss");
//带参构造方法给出的是自己设定的格式,输出的结果2022年06月07日 01:51:42
String object = sdfx. format(Date date);
//解析(从String到Date):
public Date parse(String cource)//从给定字符串的开始解析文本以生产日期
//格式:
String d6s = "1998-9-11 00:00:00";
//字符串的格式要和impleDateFormat对象设置的格式一样
SimpleDateFormat sdf3 = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
Date d6 = sdf3.parse(d6s);
System.out.println(d6);
为某一时刻和一组日历字段之间的转换提供了一些方法,并为操作日历字段提供了一些方法。
//常用方法1:get方法获取当前日历字段中的YEAR…
Calendar c= Calendar.getInstance();
//用于获取Calendar对象,其日历字段已使用当前日期和时间初始化
int Year = c.get(Calendar.YEAR);
int Month = c.get(Calendar.MONTH) + 1;//月份从0开始 所以要+1
int Day = c.get(Calendar.DATE);
System.out.println(Year + "年" + Month + "月" + Day + "日");
//常用方法2:add方法对当前时间的YEAR…进行增加或减少修改
c.add(Calendar.YEAR, -3);//当前时间的3年前
System.out.println(c.get(Calendar.YEAR));
//常用方法3:set方法:直接设置具体日期
c.set(2022,3,11);
注意:
获取某年某月有多少天:
Throwable{Error,Execption[RuntimeException,非RuntimeException]}
//Error:严重问题,不需要处理
//Exception:称为异常,表示程序本身可以处理的问题
//RuntimeException:在编译时不检查,出现问题后需要修改问题
//非RuntimeException:编译期间就必须处理,否则程序不能通过编译,即不能运行
try{
出现异常的代码
}catch (异常类名 e){ //异常类名 即 程序报错是的类名
出现异常的处理代码
}
//执行流程:程序在try里面的代码开始执行,出现异常,会自动生成一个异常类对象,该异常对象将被提交给java运行时系统,当java运行时系统接收到异常时,会到catch中找匹配的异常类,找到后进行异常的处理,执行完毕之后,程序还可以继续往下执行
e. printStackTrace()//输出异常具体信息
System.out.println(e.getMessage());//输出异常的原因
System.out.println(e.toString());//输出异常的简洁信息
throws 异常类名;
注意:这个格式是跟在方法的括号后面的
public class 异常类名 extends Exception{
无参构造
带参构造
}
一般来说,IO流的分类是按照数据类型来分的
注意:如果数据通过windows自带的记事本打开,并且可以读懂里面的内容,则使用字符流;若读不懂里面的内容,则使用字节流
File(String pathname)//通过将给定的路径名字符串转换成抽象路径名来创建新的File实例
File(String parent, String child)//通过父路径名字符串和子路径名字符串创建新的File实例
File(File parent, String child)//通过父抽象路径名和子路径名字符串创建新的File实例
File object = new File(“路径名”)
public boolean createNewFile(object)//当具有该名称的文件不存在时,创建一个由该抽象路径命名的新空文件
public boolean mkdir(object)//创建由次抽象路径名命名的目录
public boolean mkdirs(object)//创建由此抽象路径名命名的目录,包括创建必需但不存在的父目录
注意:创建路径文件就是文件,目录就是目录,不能混淆
public boolean isDirectory()//测试此抽象路径名表示的File是否为目录
public boolean isFile()//测试此抽象路径名表示的File是否为文件
public boolean exists()//测试此抽象路径名表示的File是否存在
注意:以上三个是要在绝对路径下boolean
public String getAbsolutePath()//返回此抽象路径名的绝对路径名字符串,即绝对路径
public String getPath()//将此抽象路径名转换为路径名字符串,即相对路径
public String getName()//返回由此抽象路径名表示的文件或目录的名称
public String[] list()//返回由此抽象路径名表示的目录中的文件和目录的名称字符串数组
public File[] listFiles()//返回由此抽象路径名表示的文件或目录的File对象数组
public boolean delete()//删除由次抽象路径名表示的文件或目录
注意:如果目录中有内容(目录、文件),则应该删除目录中的内容后才能再删除该目录
//字节流抽象基类:
InputStream//这个抽象类表示字节输入流的所有类的超类
OutputStream//这个抽象类表示字节输出流的所有类的超类
//子类名特点:子类名称都是以父类名作为子类名的后缀
FileOutputStream//文件输出流用于将数据写入文件
FileOutputStream object = new FileOutputStream(“绝对路径”);//创建文件输出流以指定的名称写入文件
写入字节数据:
object.write(int b)
object.write(byte[] b):写入字节数组
object.write(byte[] b, int off, int len)//将len字节从指定的字节数组开始,从偏移量off开始写入词文件输出流,一次写一个字节数组的部分数据
//注意‘A’ 对应65 A-Z是连续的,‘a’ 对应97 a-z是连续的,‘0’ 对应45 0-9是连续的
//也可以用
byte[] b = "内容".getBytes();
object.write(b,off,len);//注意:off是内容中的索引,len是添加的内容的长度
object.close()//释放资源,写完内容后,必须需要填上
写数据实现换行
//在每次写完一次数据后,添加上
object.write("x".getBytes());
//这里的x在windows系统下用\r\n;linux系统用\n;mac系统下用\r
写数据实现追加写入
FileOutputStream fos = new FileOutputStream("绝对路径",true);//即可实现追加写数据
使用try catch自行抛出异常
finally:在异常处理时提供finally块来执行所有清除操作,比如IO流中的释放资源。特点:被finally控制的语句一定被执行,除非jvm退出
try{
可能出现异常的代码
}catch(异常类名 变量名){
异常处理代码
}finally{
执行所有清除操作
}
eg
FileOutputStream fos = null;
try {
fos = new FileOutputStream("绝对路径名",true);
fos.write("内容".getBytes());
}catch (IOException io){
io.printStackTrace();
}finally {
if (fos != null) {
try {
fos.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
FileInputStream(String name)//通过打开与实际文件的连接来创建一个FileInputStream,该文件由文件系统中的路径名name命名
步骤:
BufferOutputStream//该类实现缓冲输出流,通过设置这样的输出流,应用程序可以向底层输出流写入字节,而不必为写入的每个字节导致底层系统的调用
BufferInputStream//创建BufferInputStream将创建一个内部缓冲区数组,当从流中读取或跳过字节时,内部缓冲区将根据需要从所包含的输入流中重新填充,一次很多字节
构造方法:
BufferOutputStream(OutputStream out)
BufferInputStream(InputStream in)
注意:构造方法需要的是字节流,而不是具体文件或路径,因为字节缓冲流仅仅提供缓冲区,真正的读写数据还得依靠基本的字节流对象进行操作
编码
byte[] getBytes()//使用平台的默认字符集将改String编码为一系列字节,将结果存储到新的字节数组中
byte[] getBytes(String charsetName)//使用指定的字符集将改String编码为一系列字节,将结果存储到新的字节数组中
解码
String(byte[] bytes)//通过使用平台的默认字符集解码指定的字节数组来构造新的String
String(byte[] bytes,String charsetName)//通过指定的字符集解码指定的字节数组来构造新的String
eg:
String s = "中国";
// 编码
byte[] bytes1 = s.getBytes();//这里设置的默认编码为GBK
System.out.println(Arrays.toString(bytes1));//[-42, -48, -71, -6]
byte[] bytes2 = s.getBytes("Utf-8");//设置UTF-8编码
System.out.println(Arrays.toString(bytes2));//[-28, -72, -83, -27, -101, -67]
// 解码
String ss = new String(bytes1);//使用平台默认GBK解码GBK
System.out.println(ss);
String sss = new String(bytes1, "UTF-8");//使用UTF-8解码GBK
System.out.println(sss);
String ssss = new String(bytes2);//使用平台默认GBK解码UTF-8
System.out.println(ssss);
String sssss = new String(bytes2, "UTF-8");//使用UTF-8解码UTF-8
System.out.println(sssss);
字符流抽象基类
Reader//字符输入流的抽象类
Writer//字符输出留的抽象类
字符流中编码解码相关的两个类
InputStreamReader(InputStream in)//创建一个使用默认字符集的InputStreamReader
InputStreamReader(InputStream in, String charsetName)//创建一个使用命名字符集的InputStreamReader
OutputStreamWriter(OutputStream out)//创建一个使用默认字符编码的OutputStreamWriter
OutputStreamWriter(OutputStream out, String charsetName)//创建一个使用命名字符集的OutputStreamWriter
void write(int c)//写一个字符
void write(char[] cbuf)//写一个字符数组
void write(char[] cbuuf, int off, int len)//写字符数组的一部分
void write(String str)//写一个字符串
void write(String str, int off, int len)//写一个字符串的一部分
int read():一次读一个字符数据
int read(char[] cbuf):一次读一个字符数组
构造方法:
BufferedWriter(Writer out)
BufferedReader(Reader in)
//BufferedWriter:
void newline()//写一行行分隔符,行分隔符字符串由系统属性定义
//BufferedReader:
public String readLine()//读一行文字,结果包含行的内容的字符串,不包括任何行终止字符,如果流的结尾已经到达,则为null
public static final InputStrem in//标准输入流,通常该流对应于键盘输入或由主机环境或用户指定的另一个输入源
public static final OutputStrem out//标准输出流,通常该流对应于键盘输出或由主机环境或用户指定的另一个输出源
//自己实现键盘录入数据:
BufferedReader Object = new BufferedReader(new InputStreamReader(System.in));
//java提供的一个类实现键盘录入数据:
Scanner sc = new Scanner(System.in)
//注意一般汉字的输入输出,使用的是字符流,因此需要把字节流转换为字符流
BufferedReader Object = new BufferedReader(new InputStreamReader(System.in));
//因为输入得到的是一个字符串,则如果需要得到别的类型的输出,则需要进行包装类
PrintStream ps = System.out
PrintStream(String fileName)//使用指定的文件名创建新的打印流
//自己特有的方法:
PrintStream ps =new PrintStream("文件地址");
ps.print(内容);
//使用自己特有的方法写数据,在查看数据时原样输出
PrintWriter(String fileName) //使用指定的文件名创建一个新的PrintWriter,而不需要自动执行行刷新。
public PrintWriter(OutputStream out, boolean autoFlush)//从现有的OutputStream创建一个新的PrintWriter
//out:输出流,autoFlush:一个布尔值,如果为真,则println,printf或format方法将刷新输出缓冲区
ObjectOutputStream将Java对象的原始数据类型和图形写入OutputStream。 可以使用ObjectInputStream读取(重构)对象。 可以通过使用流的文件来实现对象的持久存储。 如果流是网络套接字流,则可以在另一个主机上或另一个进程中重构对象。
构造方法
ObjectOutputStream(OutputStream out)//创建一个写入指定的OutputStream的ObjectOutputStream
序列化对象方法
void writeObject(Object obj)//将指定的对象写入ObjectOutputStream
注意:一个对象想要被序列化,该对象所属的类必须实现Serializable接口,Serializable接口是一个标记接口,实现该接口,不需要重写任何方法
ObjectInputStream反序列化先前使用ObjectOutputStream编写的原始数据和对象
构造方法
ObjectInputStream(InputStream in)//创建从指定的InputStream读取的ObjectInputStream
反序列化对象方法
void readObject(Object obj)//从ObjectInputStream读取一个对象。
Object setProperty(String key, String value)//设置集合的键和值,注意都是String类型
String getProperty(String key)//根据此属性列表中指定的键得到属性属性
Set<String> stringPropertyNames()//从该属性列表中返回所有键的集合,其中键及其对应的值是字符串
eg:
Properties prop = new Properties();
prop.setProperty("键","值");
System.out.println(prop.getProperty("键"));//根据键得到值
System.out.println(prop);
Set<String> names = prop.stringPropertyNames();//得到所以键的集合
for (String key:names){
System.out.println(key);
}
void load(InputStream inStream)//从输入字节流读取属性列表(键和元素对)
void load(Reader reader)//从输入字符流读取属性列表(键和元素对)
void store(OutputStream out, String comments)//将此属性列表(键和元素对)写入此property表中,以适合于使用load(InputStream inStream)方法的格式写入输出字节流
void store(Writer writer, String comments)//将此属性列表(键和元素对)写入此property表中,以适合于使用load(Reader reader)方法的格式写入输出字符P流
继承Thread类:
定义一个类MyThread继承Thread类
在MyThread类中重写run()方法
创建MyThread类的对象object
使用object.start()方法,自动启动线程,执行重写的run()方法
注意:run()方法是用来封装被线程执行的代码;start()方法是启动线程,然后由JVM调用此线程的run()方法的
实现Runnable类接口:
定义一个类MyRunnable实现Runnable接口
在MyRunnable类中重写run()方法
创建MyRunnable类的对象
创建Thread类的对象,把MyRunnable对象作为构造方法的参数,Thread(Runnabel)或Thread(Runnabel,””)
启动线程
相较于Thread类,实现Runnable接口的好处:避免了java单继承的局限性;适合多个相同程序代码去处理同一个资源的情况,把线程和程序的代码、数据有效分离,较好的体现了面向对象的设计思想
void setName(String name)//将此线程的名称更改为name
String getName()//返回此线程的名称
static Thread currentThread() //返回对当前正在执行的线程对象的引用Thread.currentThread().getName(),此方法可以获取main()方法所在线程名称
初始线程名称:Thread-X X=0、1、2…
注意:一般是在run()方法中获取名称,在实现类中更改名称;也可以是在继承Thread类的类中添加一个无参构造方法,和一个带参构造方法:public 类名(String name){super(name);},此时即可在创建对象的时候选择无参的使用原本的线程名,或者选择带参的使用自己设置的线程名
线程调度的两种方式:分时调度模型、抢占式调度模型(优先级)
JAVA使用的是抢占式调度模型,具有随机性
//Thread类中设置和获取线程优先级的方法:
public final int getPority()//返回此线程的优先级
public final void setPority(int newPriority)//更改此线程的优先级
注意:一般线程优先级默认为5,但执行时是随机抢占cpu的,设置优先级在[1-10]之间
static void sleep(long millis)//使当前正在执行的线程停留(暂停执行)指定的毫秒数,一般在run方法内设置
void join()//等待这个线程结束,其余线程才可以继续
void setDaemon(boolean on)//将此线程标记为守护线程,当运行的线程都是守护线程时,java虚拟机将退出
锁多条语句操作共享数据
synchronized(new Object()){
多条语句操作共享数据的代码
}
注意:如果只需要一把锁,则需要在之前定义Object obj = new Object(),然后用obj替代new Object()
好处:解决了多线程的数据安全问题
缺点:当线程很多时,因为每个线程都会去判断同步上的锁,因此会很耗费资源,降低运行效率
就是把synchronized关键字加到方法上
修饰符 synchronized 返回值类型 方法名(方法参数){
}
注意:同步方法的锁对象是this
修饰符 static synchronized 返回值类型 方法名(方法参数){ }
注意:同步方法的锁对象是类名.class
//ck实现提供比使用synchronized方法和语句可以获得的更广泛的锁定操作。
void lock() //获得锁
void unlock()//释放锁
//Lock是接口不能直接实例化,采用他的实现类ReentrantLock来实例化
//ReentrantLock的构造方法:
ReentrantLock()//创建一个ReentrantLock的实例
//Object列中的等待和唤醒方法:
void wait()//导致当前线程等待,直到另一个线程调用该对象的notify()方法或者notifyAll()方法
void notify()//唤醒正在等待对象监视器的单个线程
void notifyAll()//唤醒正在等待对象监视器的所有线程
IPv4:给每个连接在网络上的主机分配一个32bit地址,IP用2进制表示,每个IP地址长32bit,也就是4个字节,通常用十进制表示,中间用”,”分隔不同的字节
IPv6:扩大了地址空间,采用128bit地址长度,每16个字节为一组,分成8组十六进制数
常用命令
ipconfig:查看本机IP地址
ping IP地址(即查询到的本机的IPv4地址):检查网络是否连通
特殊地址:170.0.0.1:是回送地址,可以代表本机地址,一般用来测试使用
步骤
//1. 创建发送端的Socket对象
DatagramSocket ds = new DatagramSocket();
//2. 创建数据,并把数据打包
DatagramPacket dp =new DatagramPacket(bys,length,address,port);
//3. 调用DatagramSocket对象的方法发送数据
ds.send(dp);
//4. 关闭发送端
ds.close();
步骤
//1. 创建接收端的Socket对象
DatagramSocket ds = new DatagramSocket(prot);
//2. 创建一个数据包
DatagramPacket(byte[] buf, int length)
//3. 调用DatagramSocket对象的方法接收数据
ds.receive(dp)
//4. 解析数据包,并把数据在控制台显示
byte[] datas = dp.getData();
int len = dp.getLength();//getLength()返回想要发送的数据的长度或者接收到的数据的长度
String dataString = new String(datas,0,len);
System.out.println("数据是"+dataString);
//5. 关闭接收端
ds.close();
步骤:
//1. 创建客户端的Socket对象
Socket(String host, int port)
//2. 获取输出流,写数据
OutputStream getOutputStream()
//3. 释放资源
void close()
步骤:
//1. 创建服务器端的Socket对象
ServerSocket(int port)
//2. 监听客户端连接,返回一个Socket对象
Socket accept()
//3. 获取输入流,读数据,并把数据显示在控制台
InputStream getInputStream()
//4. 释放资源
void close()
组成Lambda表达式的三要素:形式参数、箭头、代码块
格式:
(形式参数)->{代码块}
//():里面没有内容,可以看成是方法形式参数为空
//->:用箭头指向后面要做的事
//{}:包含一段代码,可以看做是方法体中的内容,称为代码块
//形式参数:如果有多个参数,参数之间用逗号隔开,如果没有参数,留空即可
使用前提:有一个接口,接口中有且仅有一个抽象方法
所需类型不同
匿名内部类:可以是接口、抽象类,具体类
Lambda表达式:只能是接口
使用限制不同
如果接口中有且仅有一个抽象方法,可以使用Lambda表达式,也可以使用匿名内部类
如果接口中多于一个抽象方法,则只能使用匿名内部类
实现原理不同
匿名内部类:编译之后,会产生一个单独的.class字节码文件
Lambda表达式:编译之后,没有一个单独的.class字节码文件,对应的字节码文件会在运行的时候动态生成
//格式1
private 返回值类型 方法名(参数列表){ }
//格式2
private static 返回值类型 方法名(参数列表){ }
//注意:默认方法可以调用私有的静态方法和非静态方法,静态方法只能调用私有的静态方法
::该符号为引用运算符,而他所在的表达式被称为方法引用
eg:
//Lambda表达式
userPrintable(s -> System.out.println(s));
//分析:拿到参数s后通过Lambda表达式,传递给System.out.println方法处理
//方法引用
userPrintable(System.out::println);
//分析:直接使用System.out中的println方法来取代Lambda
推导与省略:
如果使用Lambda,那么根据可推导就可省略原则,无需指定参数类型,也无需指定重载形式,他们都将被自动推导
如果使用方法引用,也是同样可以根据上下文进行推导
即引用类的静态方法
//格式
//类名::静态方法
//eg:
Integer::parseInt Integer类的方法:public static int parseInt(String s)
//将String类型转换为int类型
//Lambda表达式被类方法替代的时候,他的形式参数全部传递给静态方法作为参数
即引用类中的成员方法
//格式
//对象::成员方法
//eg
”Hello world”::toUpperCase String类中的方法:public String toUpperCase()
// 将此String所有字符转换为大写
//Lambda表达式被对象的实例方法替代的时候,他的形式参数全部传递给该方法作为参数
即引用类中的成员方法
//格式
//类名::成员方法
//eg
String::substring String类中的方法:public String substring(int beginIndex int endIndex)
//从beginIndex开始到endIndex结束,截取字符串,返回一个子串,子串的长度为beginIndex-endIndex
//Lambda表达式被类的实例方法替代的时候,第一个参数作为调用者,后面的参数全部传递给该方法作为参数
即引用构造方法
//格式
//类名::new
//eg
Student::new
//Lambda表达式被构造器替代的时候,他的形式参数全部传递给构造器作为参数
Supplier<T>//包含一个无参的方法
T get()//获得结果
//该方法不需要参数,他会按照某种实现逻辑(由Lambda表达式实现)返回一个数据
Supplier<T>//接口也被称为生产型接口,如果我们指定了接口的泛型是什么类型,那么接口中的get方法就会生产什么类型的数据
void accept(T t) //对给定的参数执行此操作
default Consumer<T> andThen(Consumer<? super T> after) //返回一个组成的 Consumer ,依次执行此操作,然后执行 after操作
Consumer<T>//接口也被称为消费型接口,他消费的数据的数据类型由泛型指定
boolean test(T t) 在给定的参数上评估这个谓词
default Predicate<T> and(Predicate<? super T> other) :返回一个组合的谓词,表示该谓词与另一个谓词的短路逻辑AND
default Predicate<T> or(Predicate<? super T> other) :返回一个组合的谓词,表示该谓词与另一个谓词的短路逻辑或
default <T> negate() :返回表示此谓词的逻辑否定的谓词
Predicate<T>接口通常用于判断参数是否满足指定的条件
default <V> Function<T,V> andThen(Function<? super R,? extends V> after)// 返回一个组合函数,首先将该函数应用于其输入,然后将 after函数应用于结果
R apply(T t)//将此函数应用于给定的参数
Function<T,R>//接口通常用于对参数进行处理,转换(处理逻辑由Lambda表达式实现),然后返回一个新的值
//1.
Stream<T> filter(Predicate predicate)//用于对流中的数据进行过滤
Predicate接口中的方法 boolean test(T t)//对给定的参数进行判断,返回一个布尔值
//2.
Stream<T> limit(long maxSize)//返回流中的元素组成的流,截取指定参数个数的数据
//3.
Stream<T> skip(long n)//跳过指定参数个数的数据,返回由该流的剩余元素组成的流
//4.
static <T> Stream <T> concat(Stream a, Stream b)//合并a和b两个流为一个流
//5.
Stream<T> distinct()//返回由该流的不同元素(根据Object.equals(Object))组成的流
//6.
Stream<T> sorted()//返回由此流的元素组成的流,根据自然顺序排序
//7.
Stream<T>sorted(Comparator comparator)//返回由该流的元素组成的流,根据提供的Comparator进行排序
//8.
<R> Stream<R> map(Function mapper)//返回由给定函数应用于此流的元素的结果组成的流
R apply(T t)//Function接口中的方法
//9.
IntStream mapToInt(ToIntFunction mapper):返回有个IntStream其中包含将给定函数应用于此流的元素的结果
IntStream//表示原始int流,IntStream具有Stream不具有的sum()方法
int applyAsInt(T value)//ToIntFunction接口中的方法
//注意:注意对流的操作,只能进行一次,操作过一次的流就不存在了
1.void forEach(Consumer action)//对此流中的每个元素执行操作
//Consumer接口中的方法
void accept(T t)//对给定的参数执行此操作
2.long count()//返回此流中的元素数
//Stream流的收集方法:
R collect(Collector collector) //但是这个收集方法的参数是一个Collector接口
//工具类Collectors提供了具体的收集方法:
1.public static <T> Collector toList()//把元素收集到List集合中
2.public static <T> Collector toSet()//把元素收集到Set集合中
3.public static Collector toMap(Function ketMapper, Function valueMapper)//把元素收集到Map集合中
作用:负责将.class文件加载到内存中,并为之生成对应的java.lang.Class对象
JVM的类加载机制
全盘负责、父类委托、缓存机制
ClassLoader:是负责加载类的对象
java运行时具有以下内置加载器
Bootstrap class loader:虚拟机的内置类加载器,通常表示null,并且没有父null
Platform class loader:平台类加载器可以看到所有平台类,平台类包括由平台类加载器或其祖先定义的java SE平台API,其实现类和JDK特定的运行时类
System class loader:应用程序类加载器,与平台类加载器不同,系统类加载器通常用于定义应用程序类路径,模块路径和JDK特定工具上的类
类加载器的继承关系:System 的父类加载器为Platform,Platform 的父类加载器为Bootstrap
方法:
static ClassLoader getSystemClassLoader()//返回用于委派的系统类加载器
ClassLoader getParent()//返回父类加载器进行委派
首先获取到该类的字节码文件对象(即类型为Class类型的对象),然后可以通过反射去使用一个类
获取Class类型的对象方法:
使用类的class属性来获取该类对应的Class对象,eg:Student.class将会返回Student类对应的Class对象
调用对象的getClass方法,返回该对象所属类对应的
Class对象该方法是Object类中的方法,所有的java对象都可以调用该方法
使用Class类中的静态方法forName(String className),该方法需要传入字符串参数,该字符串参数的值是某个类的全路径,也就是完整包名的路径
//Class类中用于获取构造方法的方法:
//1.
Constructor<?>[] getConstructors() //返回一个包含 Constructor对象的数组, Constructor对象反映了由该 Class对象表示的类的所有公共构造函数
//2.
Constructor<?>[] getDeclaredConstructors() //返回反映由该 Class对象表示的类声明的所有构造函数的 Constructor对象的数组
//3.
Constructor<T> getConstructor(Class<?>... parameterTypes) //返回一个 Constructor对象,该对象反映由该 Class对象表示的类的指定公共构造函数
//4.
Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) //返回一个 Constructor对象,该对象反映由此 Class对象表示的类或接口的指定构造函数
//注意:
//参数:想要获取的构造方法的参数个数和数据类型对应的字节码文件对象
//Constructor类中用于创建对象的方法 T newInstance(Object... initargs):根据指定的构造方法创建对象
//Class类中获取成员变量的方法:
//1.
Field[] getFields() //返回一个包含 Field对象的数组, Field对象反映由该 Class对象表示的类或接口的所有可访问的公共字段
//2.
Field[] getDeclaredFields() //返回一个 Field对象的数组,反映了由该 Class对象表示的类或接口声明的所有字段
//3.
Field getField(String name) //返回一个 Field对象,该对象反映由该 Class对象表示的类或接口的指定公共成员字段
//4.
Field getDeclaredField(String name)// 返回一个 Field对象,该对象反映由该 Class对象表示的类或接口的指定声明字段
//注意:
//Field提供有关类或接口的单个字段的信息和动态访问
//void set(Object obj, Object value) 将指定的对象参数中由此 Field对象表示的字段设置为指定的新值。
//Class类中获取成员方法的方法:
//1.
Method[] getMethods() //返回一个包含 方法对象的数组, 方法对象反映由该 Class对象表示的类或接口的所有公共方法,包括由类或接口声明的对象以及从超类和超级接口继承的类
//2.
Method[] getDeclaredMethods() //返回一个包含 方法对象的数组, 方法对象反映由 Class对象表示的类或接口的所有声明方法,包括public,protected,default(package)访问和私有方法,但不包括继承方法
//3.
Method getMethod(String name, Class<?>... parameterTypes) //返回一个 方法对象,该对象反映由该 Class对象表示的类或接口的指定公共成员方法
//4.
Method getDeclaredMethod(String name, Class<?>... parameterTypes) //返回一个 方法对象,它反映此表示的类或接口的指定声明的方法 Class对象
//注意:
//Method在类或接口上提供有关单一方法的信息和访问权限
//Object invoke(Object obj, Object... args) 在具有指定参数的指定对象上调用此 方法对象表示的基础方法
创建模块(创建模块、创建包、创建类、定义方法)
在模块src目录下新建一个名为module-info.java的描述性文件,该文件专门定义模块名,访问权限,模块依赖等信息,描述性文件中使用模块导出和模块依赖来进行配置并使用
模块中所有未导出的包都是模块私有的,他们是不能在模块之外被访问的,在模块一下的描述性文件中配置模块导出。
模块导出格式:exports 包名;
一个模块要访问其他模块,必须明确指定依赖哪些模块,未明确指定依赖的模块不能访问。在模块二下的描述性文件中配置模块依赖。
模块依赖格式:requires 模块名;
注意:写模块名报错,需要按下Alt+Enter提示,然后选择模块依赖。
在模块二的类中使用依赖模块下的内容。
在myOne模块一下创建一个包com.service,在该包下提供一个接口,接口中定义一个抽象方法
在com.service包下创建一个包impl,在该包下提供两个实现类S1、S2
在myOne模块下的描述性文件中添加如下配置
模块导出:exports com.service;
服务提供:provides MyService with S1; 指定MyService的服务实现类是S1
在myTwo这个模块下的描述性文件中添加如下配置
声明服务接口:usesMyService;
在myTwo这个模块的类中使用MyService接口提供的服务
ServiceLoader:一种加载服务实现的工具。