欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Java基础(1)

程序员文章站 2022-04-29 18:57:36
...

计算机基础 && Java基础

1、计算机应用

1.1 科学计算
1.2 数据处理
1.3 自动控制
1.4 计算机辅助设计
1.5 人工智能
1.6 多媒体应用
1.7 计算机网络

2、计算机硬件组成部件

运算器、控制器、存储器、输入设备和输出设备。冯诺依曼体系结构
运算器+控制器–>CPU

3、软件开发

软件是按照特定顺序组织的计算机数据和指令的集合
开发是软件的制作过程

4、快捷键组合

win+D 快速显示桌面
win+L 切换用户
win+E 启动我的电脑文件夹

5、常用DOS命令

d:回车 盘符切换
dir 列出当前文件夹下的所有文件和文件夹
md 创建目录
rd 删除目录
cd … 退回上一级目录
cd \ 退回到根目录
del .txt 删除文件名后缀为.txt的文件
exit 退出dos命令行
cls 清屏

6、java语言平台版本

6.1 J2SE标准版,是为开发普通桌面和商务应用程序提供的解决方案,偏桌面应用程序开发
6.2 J2ME小型版,是为开发电子消费产品和嵌入式设备提供的解决方案
6.3 J2EE企业版,是为开发企业环境下的应用程序提供的一套解决方案,偏Web应用程序开发

7、java特点

开源、跨平台、可移植性、面向对象、分布式处理、结构中立、多线程等
一处编译,到处运行(只需要安装一个JVM java虚拟机就可以)

8、

JRE Java Runtime Environment java运行环境,包括JVM和Java的核心类库
JDK Java开发工具包,包括JRE
使用JDK开发完成的java程序,交给JRE去运行。

9、JDK目录

9.1 bin目录:存放一些可执行程序
9.2 db目录:小型数据库。从JDK 6.0开始,引用的一个纯Java实现、开源的数据库管理系统,支持JDBC 4.0的所有规范
9.3 jre目录:Java运行环境
9.4 include目录:JDK通过C/C++实现,启动时需要引入一些C的头文件
9.5 lib目录:Java类库
9.6 src.zip文件:src文件夹的压缩文件,放置JDK核心类的源代码

10、HelloWorld

class HelloWorld{
	public static void main(String[] args){
		System.out.println("HelloWorld");
	}
}

编译:javac helloworld.java
运行:java helloworld

11、常见基础错误

11.1 找不到文件:文件扩展名隐藏导致编译失败、文件名写错
11.2 单词拼写错误:class写成Class、String写成string、System写成system、main写成mian等
11.3 括号匹配问题
11.4 中英文问题:提示信息错误:非法字符:???的格式,因为出现中文的标点符号

12、Java注释

12.1 单行注释://注释文字
12.2 多行注释:/* 注释文字 /
12.3 文档注释:/
* 注释文字 */

13、

包:其实就是文件夹。用于解决相同类名问题
包名要求全部小写
类或接口采用驼峰命名,每个单词首字母都大写
方法或变量,第二个单词开始首字母大写
常量,字母全部大写,单词将使用_分开(constant 常量)

14、进制与进制转换

二进制表示形式:0b数字 or 0B数字
八进制表示形式:0数字
十六进制表示形式:0x数字 or 0X数字

系数:每一位上的数据
基数:X进制,基数就是X
权:在右边,从0开始编号,对应位上的编号就是该为的权
结果:系数*基数的权次幂相加

15、原码、反码、补码

原码:二进制定点表示法,最高位为符号位,0正1负
反码:正数反码=原码;负数反码是对原码除符号位以外逐位取反
补码:正数补码=原码;负数补码是在其反码的末位+1

16、32位/64位系统下数据类型大小

数据类型 32位 64位
byte 1字节 1字节
char 2字节 2字节
char* 4字节 8字节
boolean 1字节 1字节
short 2字节 2字节
int 4字节 4字节
long 4字节 8字节
long long 8字节 8字节
float 4字节 4字节
double 8字节 8字节

java中整数默认数据类型是int类型,小数默认数据类型是double类型

其中float占4个字节,即32个二进制位。
1位代表符号位,8位代表指数位,23位代表尾数位
8位代表指数位:00000000 - 11111111,0 - 255,其中0代表0,255代表无穷大。1 - 254表示-126 - 127。

ASCII码中48对应数字0,65表示大写字母A,97表示小写字母a

17、

强制类型转换过程中如果是低精度类型转高精度类型,正常;高精度类型转低精度类型,可能会导致精度丢失,并且产生错误的值,比如 byte a=(byte)(126+4);的结果是-126,因为byte将int类型的第一个字节的第一位作为符号位使用,因为在运算过程中是采用的补码方式,所以int类型的第一个字节的第一位为1,会被byte作为负号使用。
byte与byte或者short、char、int等类型(计算类型为变量时)计算时,都会隐式的提升为int
类型提升规律:
byte,short,char --> int --> long --> float --> double

18、

局部变量在使用之前必须赋值。

19、

&&具有短路效果。左边false,右边不执行。&&与&的结果相同
位运算符:按位与&,按位或|,按位异或^,按位取反~,有符号右移>>,无符号右移>>>,有符号左移<<
一个数对另一个数位异或两次,该数不变。

19.1 两值交换(不使用额外变量空间)

x=x+y; y=x-y; x=x-y;
或者 x=x^y; y=x^y; x=x^y;

19.2 最有效率的计算2 * 8的结果

2 << 3

20、

int x=10;其实是两条语句,一条声明,一条赋值

21、

在语句之前可以添加mark标记,格式为标号名:,标号只要是合法的标识符即可
标号可以用来break跳出多个循环等功能。
e.g.
a: for(…) …

22、

数值类型的默认初始化值
byte、short、int、long默认初始化值为0
float、double默认初始化值为0.0
boolean默认初始化值为false
char默认初始化值为’\u0000’
\u0000,每个0
其实代表的是16进制的0,那么四个0就是代表16个二进制。
直接输出数组变量我们可以得到类似于这样的值:[[email protected]
其中[代表是数组,几个就代表几维;I代表是int类型;@是固定的;19bb25a代表的是十六进制的地址值。
数组创建以后都会先赋予初始化默认值。即使是以直接赋值方式创建的数组,也会先经过初始化默认值的步骤。

23、

java中到底是传值还是传地址
23.1 一种说法是既是传值,也是传地址,基本数据类型传递的是值,引用数据类型传递的是地址
23.2 另一种说法是由高司令(java之父)说的,java中只有传值,因为地址值也是值

24、

构造函数,如果我们创建了构造函数(有参数,或者无参数),系统都不会再提供默认的无参数的构造函数。
如果一个类中所有属性和方法都是静态的,那么为了避免类被创建对象,可以定义一个私有的构造方法

25、

创建一个对象的步骤
25.1 主类在内存的方法区中开辟一块区域
25.2 main函数进栈,对象类在内存的方法区中开辟一块区域
25.3 类在栈中声明一个引用
25.4 对象在堆中开辟一块区域,并对对象的类属性默认初始化,在显式触发赋值
25.5 对象的类构造方法进栈,执行构造方法,结束以后出栈
25.6 将创建的对象地址赋值给引用

26、

文档注释/** 注释内容 */
说明书:
.java文件头部
@author
@version
方法头部
@param
@return

javadoc -d api -version -author 文件名.java

27、

代码块根据引用分为局部代码块,构造代码块,静态代码块

在类中方法外创建构造代码块,那么每创建一次对象(或是调用构造)就会执行一次,优先于构造函数执行。形式是直接在类中{ … }

静态代码块形式是static{ … },并且只随着类(类名.class)的加载而加载,优先于构造函数以及主函数执行,仅执行一次。作用一般是来给类进行初始化,比如加载驱动。

父类静态代码块 --> 子类静态代码块 --> 父类构造代码块 --> 父类构造方法 --> 子类构造代码块 --> 子类构造方法

28、继承extends

好处:提高代码的复用性和维护性,是面向对象编程中多态的前提
弊端:提高了类的耦合关系
28.1 子类只能继承父类中所有非private的成员
28.2 子类不能继承父类的构造方法,但是可以通过super关键字去访问父类构造方法

29、this和super

this:代表当前对象的引用,谁来调用我,我就代表谁
super:代表当前对象父亲的引用
区别:
29.1 this.成员变量调用本类的成员变量,也可以调用父类的成员变量;super.成员变量调用父类的成员变量
29.2 this(…)调用本类的构造方法;super(…)调用父类的构造方法(PS:this(…)或super(…)必须出现构造方法的第一条语句,因此不能同时出现;当然不是只有类的构造函数才可以使用this和super的)
29.3 this.成员方法调用本类的成员方法,也可以调用父类的方法;super.成员方法调用父类的成员方法
29.4 每个构造方法的第一条语句,都会加上可缺省的默认super()(即使不存在,也会默认加上) Object类最顶层的父类,用来访问父类中的空参构造函数。

30、final

30.1 final修改类,类不能被继承
30.2 final修饰变量,变量就变成了常量,只能被赋值一次;基本类型,是值不能被改变;引用类型,是地址值不能被改变,对象中的属性可以改变。
30.3 final修饰方法,方法不能被重写
30.4 final修饰变量的初始化时机:显示初始化;在对象构造完毕前即可(如果显示初始化,那么不能二次赋值)。

31、多态、抽象类与接口

父类 对象名 = new 子类();
对象名.成员变量   --> 父类的成员变量
对象名.成员方法   --> 子类的成员方法

31.1 成员变量:编译看左边(父类),运行看左边(父类)
31.2 成员方法:编译看左边(父类),运行看右边(子类) 动态绑定
31.3 静态方法:编译看左边(父类),运行看左边(父类)
(PS:如果是子类中特有方法,那么只能用子类对象去调用)
31.4 多态中向上转型和向下转型
父类 对象名1=new 子类(); 父类引用指向子类对象就是向上转型
父类 对象名2=(父类)对象名1; 向下转型(PS:先有向上转型,后有向下转型)
31.5 开发的时候很少在创建对象的时候用子类引用指向指向对象,直接创建子类对象更方便,可以只用子类中和特有属性和行为;一般是当做方法参数的时候用多态最好,因为扩展性强
PS:关键字instanceof判断前边对象时候是后边的数据类型
PS2:一个类不写继承任何类,那么默认继承Object类
31.6 interface接口中
成员变量:只能是常量,并且是静态且公共的;默认修饰符为public static final
成员方法:只能是抽象方法;默认修饰符为public abstract
PS:接口不可用implements接口,但是可以extends接口,并且可以继承多个接口
31.7 抽象和接口的设计理念
抽象类 被继承体现的是:is a的关系。抽象类中定义的是该继承体系的共性功能。
接口 被实现体现的是:like a的关系。接口中定义的是该继承体系的扩展功能。

32.0 四种权限修饰符及其访问范围

本类 同一个包下(子类和无关类) 不同包下(子类) 不同包下(无关类)
private Y
默认(无修饰符) Y Y
protected Y Y Y
public Y Y Y Y

32、类及其组成所使用的常见修饰符

32.1 修饰符

权限修饰符:private,默认,protected,public
状态修饰符:static,final
抽象修饰符:abstract

32.2 类

权限修饰符:默认修饰符,public
状态修饰符:final(最终类,不能被继承)
抽象修饰符:abstract(抽象类)

32.3 成员变量

权限修饰符:private,默认,protected,public
状态修饰符:static(静态变量),final(常量)

32.4 构造方法

权限修饰符:private(其他类不能用本类创建对象,一般当本类中所有方法都是静态的),默认,protected,public

32.5 成员方法

权限修饰符:private,默认,protected,public
状态修饰符:static(静态成员方法),final(最终成员方法,不能被重写)
抽象修饰符:abstract(抽象方法)

32.6 除此以外的组合规则

成员变量:public static final(公共静态常量)
成员方法:public static、public abstract、public final

33、内部类

33.1 在类内部定义的类
33.2 内部类访问特点
内部类可以直接访问外部类的成员,包括私有;
外部类要访问内部类的成员,必须创建对象;
外部类名.内部类名 对象名 = 外部类对象.内部类对象;
33.3 内部类可以私有,一旦内部类私有化,那么就不能被在外部类以外实例化,但是可以在外部类的成员方法中实例化调用内部类的成员变量和方法。
33.4 内部类可以static,一旦内部类static,那么根据静态方法和非静态方法构建方法编写:外部类名.内部类名 对象名 = 外部类名.内部类对象;
如果同时内部类方法也是静态方法,那么调用格式为:外部类名.内部类名.静态方法名();
33.5 内部类测试题:
//要求:使用已知的变量,在控制台输出30,20,10

class Outer {
	public int num = 10;
	class Inner {
		public int num = 20;
		public void show() {
			int num = 30;
			System.out.println(num);
			System.out.println(this.num);
			// 内部类之所以能获取外部类的成员,是因为他能获取到外部类的引用:外部类名.this
			System.out.println(Outer.this.num); 
		}
	}
}

33.6 局部内部类:在方法中定义的内部类,只能在其所在的方法中访问。
局部内部类访问外部类的局部变量必须使用final修饰;
局部内部类在访问他所在方法中和局部变量必须使用final,因为当调用这个方法时,局部变量如果没有使用final修饰,他的生命周期和方法的生命周期是一样的。当方法弹栈,这个局部变量也会消失,那么如果局部内部类对象还没有马上消失想用这个局部变量,就没有了;如果用final修饰会在类加载的时候进入常量池,即使方法弹栈,常量池的常量还在,也可以继续使用。
PS:在jdk1.8中取消了这样的机制,局部变量也可以被局部内部类访问。
33.7 匿名内部类:就是内部类的简化写法;前提是存在一个类或接口(这个类可以是具体类也可以是抽象类);格式: new 类名或接口名(){重写方法;};其本质是一个继承了该类(new 类名)或者实现了该接口(new 接口)的子类匿名对象。
(PS:应用场景:监听,多线程等 )
e.g.

interface Inter{ public void print();}
class Outer{
	class Inner  implements Inter{ 
		public void print(){
			System.out.println("print");
		}
	}
	public void method(){
		new Inter(){
			public void print(){ 
				System.out.println("print Inter"); 
			}
		}
	}.print();
}
// Compared case
new Inner().print();

//便捷化调用方法
Inter i = new Inner(){ … }; //父类引用指向子类对象
i.print();
// 但是当方法是匿名内部类特有时,不能使用这种方法;匿名内部类是不能向下转型的,因为没有子类类名
}
因为匿名内部类中存在多个方法的时候,每次使用都需要重写其中的所有方法在调用,所有建议匿名内部类中一般只包含一个未实现的方法

e.g.

class Main {
	public static void main(String[] args) {
		//如何调用PersonDemo中的method方法
		PersonDemo pd = new PersonDemo();
		// method one
		pd.method(new Student());
		// method two
		pd.method(new Person(){			
		// 匿名内部类当做参数传递,本质是把匿名内部类看做为一个对象来使用
			public void show() {
				System.out,println("show");
			}
		});
	}
}
abstract class Person {
	public abstract void show();
}
class PersonDemo {
	public void method(Person p) {
		p.show();
	}
}
//method one
class Student extends Person {
	public void show() {
		System.out,println("show");
	}
}

34、eclipse快捷键

新建 ctrl+n
格式化 ctrl+shift+f
导入包 ctrl+shift+o
注释 ctrl+/,ctrl+shift+/,ctrl+shift+
代码上下移动 选中代码alt+上/下箭头
查看源码 选中类名F3/ctrl+鼠标点击
查找具体的类 ctrl+shift+t
查找具体类的具体方法 ctrl+o
给建议 ctrl+l,根据右边生成左边的数据类型,生成方法
删除代码 ctrl+d
抽取方法 alt+shift+m
改名 alt+shift+r
syso System.out.println(); 快捷键
快速创建无参构造方法,有参构造方法,成员变量的get和set方法
alt+shift+s,+c:Generate Constructors from Superclass…
alt+shift+s,+o:Generate Constructor using Field…
alt+shift+s,+r:Generate Getters and Setters…

35、

API 应用程序接口,一般指的是具体的类(这些类将底层的实现封装起来),如String类等

36、Object类的方法

hashCode()方法:返回该对象的哈希码值。默认情况下,该方法会根据对象的地址来计算。PS:不同对象hashCode,一般不同;同一对象的hashCode一定相同 。
getClass()方法:返回此对象的运行时类
getName()方法:以String的形式返回此Class对象所表示的实体(类,接口等)名称
getString()方法:getClass().getName()+"@"+Integer.toHexString(hashCode()); // toHexString() 十六进制返回;PS:如果直接输出对象,会默认调用toString()方法
equals()方法 比较的是地址值,依赖的底层是==号;PS:==号是比较运算符,既可以比较基本数据类型(值),也可以比较引用数据类型(地址值)

37、Scanner

第一次录入int,第二次录入String会出错。因为nextInt()是键盘录入整数的方法,之后我们会习惯性的回车,这个时候键盘上录入\r\n,nextLine()就录入了\r\n。而同时nextLine()录入\r\n,就证明一行结束。

38、String

如果两个String类型s1,s2值相同(比如都等于abc),那么s1==s2和s1.equals(s2)都为true。因为此时abc在常量池,并且s1和s2都指向abc。
String s=new String(“abc”); 该过程中创建了两个对象。现在常量池中创建abc,然后在堆中创建也一个abc的String类型的对象,并且s指向堆内存中的对象。
String s1=new String(“abc”); String s2=“abc”; s1==s2为false;s1.equals(s2)为true;
String s2=“abc”; String s3=“a”+“b”+“c”; s2==s3为true;因为java中有常量优化机制
String s1=“ab”; String s2=s1+“c”; String s3=“abc”; s2==s3为false;因为s1+“c"先转换成StringBuilder或者StringBuffer(通过append()方法添加"c”),然后通过toString()方法转成String类型的对象
如果String对象被赋值null,那么不能调用任何的方法,否则会出现NullPointerException的报错

39、StringBuffer

是线程安全的可变字符序列,StringBuffer sb
查看初始容量:sb.capacity() 如果不设置初始容量,那么默认大小为16
容器中的字符个数:sb.length( )
sb.append(str); 将任意类型数据添加到字符串缓冲区,并返回字符串缓冲区本身;只要是使用sb对象,那么每次返回的缓冲区都是相同的
sb.substring(); 返回一个String字符串

40、String、StringBuffer和StringBuilder的区别

String是一个不可变的字符序列;StringBuffer和StringBuilder是可变的字符序列;
StringBuffer是jdk1.0版本的,是线程安全的,效率低;StringBuilder是jdk1.5版本的,是线程不安全的,效率高。

41、

Array.sort():在小数组的情况下使用快排(一般阀值是7),其他情况下使用归并排序

42、正则表达式

匹配qq号(要求:长度5-15,首字母不能为0,全部数字):[1-9]\d{4, 14}
[]代表单个字符;.代表任意字符;()代表一组字符(e.g. (.)\1 代表第一组的那一个任意字符再重复出现一次)
直接拼接,并集;&&,交集;^,非;
\d,数字[0-9];\D,非数字[0-9];\s,空白字符[\t\n\x0B\f\r];\S,非空白字符[\s];\w,单词字符[a-zA-Z_0-9];\W,非单词字符[^\w]
其中x代表字符:x?,x一次或一次也没有;x*,x零次或多次;x+,x一次或多次;x{n},x恰好n次;x{n,},x至少n次;x{n, m},x至少n次,但是不超过m次
叠词的正则表达式(e.g. 快快乐乐):(.)\1(.)\2

43、Pattern

创建Pattern对象:Pattern p = Pattern.compile(正则表达式);
获取匹配器:Matcher m = p.matcher(字符串);
是否匹配:boolean b = m.matches();
find():下一个类型匹配的字符串是否存在
group():返回下一个类型匹配的字符串

44、System类

gc() 调用垃圾回收器
finalize() 当垃圾回收器判断垃圾太多的时候,会被调用并唤醒垃圾回收器
exit(status) 强制退出java虚拟机,status非零,表示异常退出
currentTimeMillis() 当前毫秒数==new Date().getTime()
arraycopy(src, srcPos, dest, destPos, length) 拷贝数组,原数组src从srcPos开始选择length长度拷贝到目标数组dest中从destPos开始

45、BigInteger类

new BigInteger(String)
add() 加;subtract() 减;multiply() 乘;divide() 除。

46、BigDecimal类

因为double或float计算并不能得到正确的结果(只能接近),因此为了得到更加精确的结果,可以使用BigDecimal。
PS:如果直接使用new BigDecimal(double)并不能保证得到完全精确的结果,因此在实际开发中并不推荐使用;推荐使用new BigDecimal(String)或者BigDecimal.valueOf(double)

47、Calendar类

Calendar c = Calendar.getInstance(); //父类引用指向子类对象GregorianCalendar
e.g. 判断该年是闰年还是平年
思路:获取该年3月1日-1,那么得到的二月份是28天就是闰年,29天就是平年

Calendar c = Calendar.fetInstance();
c.set(year, 2, 1); //设置为那一年的3月1日
c.add(Calendar.DAY_OF_MONTH, -1); //将日期向前减去1天
if(c.get(Calendar.DAY_OF_MONTH) == 29)
	//平年
else
	//闰年

48、数组与集合

数据既可以存储基本数据类型,也可以存储引用数据类型;集合只能存储引用数据类型(对象)。
数组长度是固定的,不能自动增长;集合的长度是可变的,可以根据元素的增加而增加。

49、集合Collection(单列集合的根接口)

List:有序(存取的顺序一致),有索引,可以存储重复(允许存储相同的值)
在使用迭代器Iterator对List进行迭代的时候,不能使用List的方法对List的内容长度进行变更(add,remove),但是可以使用ListIterator li = list.listIterator(); 通过迭代器ListIterator对list进行操作

–ArrayList:底层数组实现,查询快,增删慢。线程不安全,效率高。
----1.1 并:将集合添加到另一个集合中,只是添加元素过去使用addAll()方法,如果是作为对象添加过去使用add()方法
----1.2 差:removeAll()
----1.3 交:retainAll() 如果调用的集合改变就返回true,不变返回false
----1.4 是否包含:containAll()
–LinkedList:底层链表实现,查询慢,增删快。线程不安全,效率高。
–Vector:底层数组实现,查询快,增删慢。线程安全,效率低。
PS: 线程安全导致查询较慢
PS: 查找和修改多,用ArrayList;增删多,用LinkedList;都多,用ArrayList。
Set:无序,无索引,不可存储重复
–HashSet:底层哈希算法
----LinkedHashSet 使用链表实现的HashSet:是set集合中唯一一个能保证怎么存就怎么取的集合对象,原理与HashSet相同。
–TreeSet:底层二叉树算法,主要用来对元素进行排序,并且保证元素唯一
PS: 一般在开发中无需对存储元素排序,多使用HashSet;面试的时候,多问TreeSet,有几种排序方式,以及几种排序方式的区别
PS: 去除eclipse中黄线检查在public class上面加上@SuppressWarnings({ “rawtypes”, “unchecked” })

50、泛型

将类型由原来的具体的类型参数化,类似于方法中的变量参数,此时类型也定义成参数形式(可以称之为类型形参),然后在使用/调用时传入具体的类型(类型实参)。
使用方式:第一种类的泛型:
e.g.

public class Test<Q>{
	private Q tq;
	public Q getValue(){
		return tq;
	}
}

第二种方法泛型:
e.g.

public<T> void show(T t){
	Systom.out.println(t);
}

PS:类的静态方法需要创建自己的泛型,其中允许泛型名称与类泛型名称声明相同,但是实际表示不同
泛型通配符<?>,表示任意类型,如果没有明确,那么就是Object以及任意的Java类
? extends E,泛型固定上边界,向下限定 E及其子类
e.g.

ArrayList<Student> list1 = new ArrayList<>();
list1.add(new Student("小白",11));
...
ArrayList<BaseStudent> list2 = new ArrayList<>();
list1.add(new BaseStudent("小黑",16));
...
list1.addAll(list2);

? super E,泛型固定上边界,向上限定 E及其父类
e.g.

class CompareByAge implements Comparator<Student>{
	@Override
	public int compare(Student s1, Student s2){
		int num = s1.getAge() - s2.getAge();
		return num == 0 ? s1.getName().compareTo(s2.getName()) : num;
	}
}
public static void main(String[] args){
	// 按照比较器排序
	TreeSet<Student> ts1 = new TressSet<>(new CompareByAge());
	ts1.add(new Student("小白",11));
	ts2.add(new Student("小黑",16));
	...
	// 同样按照比较器排序
	TreeSet<BaseStudent> ts2 = new TressSet<>(new CompareByAge());
	ts1.add(new BaseStudent("小黄",21));
	ts2.add(new BaseStudent("小红",13));
	...
}

51、静态导入

导入类中的静态方法,格式:import static 包名…类名

52、可变参数

可变参数其实是一个数组,并且必须在参数列表的最后一个,格式为:数据类型…可变参数名

53、Map

HashMap 底层是哈希算法,针对键
–LinkedHashMap,底层是链表,针对键
TreeMap 底层是二叉树算法,针对键,及需要排序的情况
PS: 开发中使用HashMap较多,面试时TreeMap较多

Map类的集合不存在iterator()方法,但是可以使用keySet()方法得到Set集合来迭代。
e.g.

Set<String> keySet = map.keySet();
Iterator<String> it = keySet.iterator();
while(it.hasNext()){
	String key = it.next();
	Integer value = map.get(key);
}

或者可以使用entrySet()方法将键值对转换为映射项(键值对)Entry对象,然后迭代每一Entry对象,通过getKey()和getValue()方法分别获取键和值
e.g.

Set<Map.Entry<String, Integer>> entrySet = map.entrySet();
Iterator<Map.Entry<String, Integer>> it = entrySet.iterator();
while(it.hasNext()){
	Map.Entry<String, Integer> en = it.next();
	String key = en.getKey();
	Integer value = en.getValue();
}

54、Map和Collection接口的不同

Map是双列的,Collection是单列的
Map的键唯一,Collection的子体系Set是唯一的
Map集合的数据结构值针对键有效,与值无关;Collection集合的数据结构是针对元素有效的。
PS:HashSet底层继承的是HashMap

55、HashMap和Hashtable的区别

共同点:底层都是法系算法,都是双列集合
区别:

  1. HashMap是线程不安全的,效率高,JDK1.2版本;Hashtable是线程安全的,效率低,JDK1.0版本。
  2. HashMap可以存储null键和null值(为了后期代码可以执行);Hashtable不可以存储null键和null值。

56、Collections类中常见方法

// 排序
public static <T> void sort (List<T> list)
// 二分查找:不存在就返回(-(插入点)-1)  PS:插入点为第一个大于此键的元素索引;
// 如果列表中都小于它,那么就为 list.size()
public static <T> int binarySearch(List<T> list, T key)
// 最大值
public static <T>  T max(Collection<T> coll)
// 元素顺序反转
public static void reverse(List<?> list)
// 随机替换,类似于洗牌
public static void shuffle(List<?> list) 

57、示例:模拟斗地主——洗牌、发牌、看牌(牌排序)

public class Main{
	public static void main(String[] args){
		String[] num = {"A", "2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K"};
		String[] color = {"红桃", "黑桃", "方片", "梅花"};
		ArrayList<String> poker = new ArrayList<>();
		
		// 拼接花色和数字
		for(String s1 : color){
			for(String s2 : num){
				poker.add(s1.concat(s2));
			}
		}
		poker.add("小王");
		poker.add("大王");

		// 洗牌
		Collections.shuffle(poker);

		// 发牌
		ArrayList<String> player1 = new ArrayList<>();
		ArrayList<String> player2 = new ArrayList<>();
		ArrayList<String> player3 = new ArrayList<>();
		ArrayList<String> lastCards = new ArrayList<>();

		for(int i = 0; i < poker.size(); i++){
			if(i >= poker.size() - 3){
				lastCards.add(poker.get(i));
			} else if(i % 3 == 0){
				player1.add(poker.get(i))
			} else if(i % 3 == 1){
				player2.add(poker.get(i))
			} else {
				player3.add(poker.get(i))
			}
		}
		
		// 看牌
		System.out.println("player1:"+player1);
		System.out.println("player2:"+player1);
		System.out.println("player3:"+player1);
		System.out.println("lastCards:"+lastCards);

// ---------------------------------------------------------------------------------------------------
// ---------------------------------------------牌排序---------------------------------------------
// ---------------------------------------------------------------------------------------------------
		// 存储索引和扑克牌
		HashMap<Integer, String> hm = new HashMap<>();
		// 存储索引
		ArrayList<String> list = new ArrayList<>();
		int index = 0;
		
		// 拼接花色和数字
		for(String s1 : color){
			for(String s2 : num){
				list.add(index);
				hm.put(index++, s2.concat(s1));
			}
		}
		list.add(index);
		hm.put(index++, "小王");
		list.add(index);
		hm.put(index++, "大王");
		
		// 洗牌
		Collections.shuffle(list);

		// 发牌
		TreeSet<Integer> player1 = new TreeSet<>();
		TreeSet<Integer> player2 = new TreeSet<>();
		TreeSet<Integer> player3 = new TreeSet<>();
		TreeSet<Integer> lastCards = new TreeSet<>();
		
		for(int i = 0; i < list.size(); i++){
			if(i >= list.size() - 3){
				lastCards.add(list.get(i));
			} else if(i % 3 == 0){
				player1.add(list.get(i))
			} else if(i % 3 == 1){
				player2.add(list.get(i))
			} else {
				player3.add(list.get(i))
			}
		}
		
		// 看牌
		lookPoker(hm, player1, "玩家1");
		lookPoker(hm, player2, "玩家2");
		lookPoker(hm, player3, "玩家3");
		lookPoker(hm, lastCards, "底牌");
	}
	
	// 看牌(牌排序)
	public static void lookPoker(HashMap<Integer, String> hm, TreeSet<Integer> ts, String name){
		System.out.print(name + "的牌是:");
		for(Integer i : ts){
			System.out.print(hm.get(i) + " ");
		}
		System.out.println();
	}
}

57、补充:线程安全与不安全

  1. 线程安全:就是多线程访问时,采用了加锁机制,当一个线程访问该类的某个数据时,进行保护,其他线程不能进行访问直到该线程读取完,其他线程才可使用。不会出现数据不一致或者数据污染。
  2. 线程不安全:就是不提供数据访问保护,有可能出现多个线程先后更改数据造成所得到的数据是脏数据线程不安全:就是不提供数据访问保护,有可能出现多个线程先后更改数据造成所得到的数据是脏数据
相关标签: java基础