安装
Jdk8:https://www.oracle.com/cn/java/technologies/javase/javase8-archive-downloads.html
notepad++ :https://notepad-plus.en.softonic.com/?ex=DINS-635.0
Hbuilderx:https://www.dcloud.io/hbuilderx.html
idea下载:https://www.jetbrains.com/idea/download/#section=mac
Path环境变量配置
Mac
第一次无需配置环境变量
-
查看JDK安装位置
# 进入 JDK 安装目录 cd /Library/Java/JavaVirtualMachines # 查看文件 ls
-
打开环境变量文件
# 进入当前用户的 home 目录 cd /Users/修改为自己 MAC 电脑用户名称 # 输入 cd ~ # 打开环境变量配置文件 open .bash_profile # 报错:.bash_profile does not exist. # 第一次配置环境变量,先创建文件 touch .bash_profile # 再次执行打开环境变量配置文件
-
配置JDK环境变量
# 复制如下内容,JAVA_HOME 替换为自己目录 export JAVA_HOME=/Library/Java/JavaVirtualMachines/jdk1.8.0_281.jdk/Contents/Home export PATH=$JAVA_HOME/bin:$PATH:. export CLASSPATH=$JAVA_HOME/lib/tools.jar:$JAVA_HOME/lib/dt.jar:. # 配置文件立即生效 source .bash_profile # 查看 JAVA_HOME 目录 echo $JAVA_HOME
-
检查环境变量
# 查看 JDK 版本信息 java -version
window
JAVA_HOME:配置jdk安装文件路径
PATH:%JAVA_HOME%\bin
CLASSPATH:.;%JAVA_HOME%\lib
配置好环境变量后在cmd输入java还有javac查看是否配置成功
查看Java版本号:java -version
Mac快捷键
command + shift + c 退到根目录
Command + option + c 复制路径
command + shift + G 前往文件夹
win常用DOS命令
dir 查看当前路径下的内容
cd 目录路径 进入该目录
cd … 回退到上一级目录
cd \ 回退到盘符目录
cls 清屏
exit 退出命令提示符窗口
Java基础语法
注释
// 单行注释
/*多行注释*/
/**文档注释*/
命名规则
命名的时候要避免使用关键字和保留字
- 数字不可开头
- 不可用关键字、保留字命名
- 可使用数字、字母、下划线,$
- 区分大小写,一般首字母小写
- 见名知意
变量
定义变量
/* 数据类型 变量名 = 初始化值; */
int age = 10;// 声明变量并赋值
/*
先声明,后赋值(使用前赋值即可)
数据类型 变量名;
变量名 = 初始化值;
*/
double money;
money = 99.9;
/*
同一行定义多个同一种数据类型的变量,中间使用逗号隔开。【不建议使用,降低程序的可读性】
*/
int num1 = 10, num2 = 20;
int num3,num4;
num3 = 30;
num4 = 40;
使用变量的注意事项
- 同一对{}中,变量名不可重复。
- 使用变量前,必须初始化(赋值)。
- 定义long类型变量,需在整数后价L(大小写均可,建议大写)。
- 定义float类型变量,需在后面加F(同上)
常量
在程序运行过程中,其值不可以发生改变的量。
/*
* final声明的是常量,值不可改变,一般常量命名都大写
* */
final int NUM = 12;
数据类型
1byte = 8bit (8位1字节,在取值范围内赋值)
在java中整数默认是int类型,浮点数默认是double类型
/*基本数据类型:
* 数值类型:
* 整数类型:
字节型:byte(1字节)【取值范围:-128~127】、
* 短整型:short(2字节)【取值范围:-32768~32767】、
* 整型:int(4字节)【取值范围:-2的31次方到2的31次方-1】、
* 长整型:long(8字节)【取值范围:-2的63次方到2的63次方-1】
* 实数类型:
单精度浮点型:float(4字节)【正数到10的38次方】、
双精度浮点型:double(8字节)【正数到10的308次方】
* 字符类型:char(2字节):取值范围0-65535
* 布尔类型:boolean(1字节):true、false
* */
byte b1 = 127;
short s1 = 32767;
int i11 = 123;
long l1 = 1;
float f1 = 2.12f;// float值是浮点数,后面需加f
double f2 = 2.12;// 开发常用double声明浮点数
// 字符类型char 用于存放单个字符用''括住 或者用于存放ASCII码
char c1= '9';// ''中放一个字符
char c2 = 86;// ASCII码表
System.out.println(c1);
System.out.println(c2);
boolean bo1 = true;// 布尔类型
类型转换
boolean类型不能与其他基本数据类型相互转换
自动转换:
小范围的赋值给大范围的,直接书写即可
double num1 = 10;// 将int类型的10直接赋值给double类型
强制转换:
大范围的赋值给小范围的,需强制转换
语法:目标数据类型 变量名 = (目标数据类型)值或变量名;
double num20 = 5.5;
int num21 = (int)num20;// 小数位直接舍弃
tip:
-
char类型的数据转换为int类型是按照码表中对应的int值进行计算的。比如在ASCII码表中,'a’对应97。
int a = 'a'; System.out.println(a); // 将输出97 char b = 97; System.out.println(b); // 将输出a
-
整数默认是int类型,byte、short和char类型数据参与运算均会自动转换为int类型。
byte b1 = 10; byte b2 = 20; byte b3 = b1 + b2; // 第三行代码会报错,b1和b2会自动转换为int类型,计算结果为int,int赋值给byte需要强制类型转换。 // 修改为: int num = b1 + b2; // 或者: byte b3 = (byte) (b1 + b2); int a = 1; float b = 2.0f; double c = a + b;
运算符
算术运算符
+、-、*、/、%
**tip:**整数操作只能得到整数;想得到小数,须有浮点数参与运算。
字符的“+”操作
char数据类型可参与算术运算,根据ASCII码表对应的数值进行计算。(十进制)
需记忆以下三个字符对应的数值:
- ‘a’ – 97,a-z对应97-122,97为a,98为b,依次递加
- ‘A’ – 65,A-Z对应65-90,同上
- ‘0’ – 48,0-9对应48-57,同上
char str1 = 'a';
System.out.println(str1+1);// 输出98
算术运算自动转换等级顺序:
byte,short,char,int,long,float,double
字符串的“+”操作
当“+”操作中出现字符串时,“+”即为连接符
当连续进行“+”操作时,则从左到右逐个执行
System.out.println(1+99+"哈哈");// 输出100哈哈
System.out.println(1+2+"it"+3+4);// 输出3it34
// 可使用小括号改变运算的优先级
System.out.println(1+2+"it"+(3+4));// 输出3it7
赋值运算符
=、+=、-=、*=、/=、%=
自增、自减
++ // 自增:变量的值+1
-- // 自减:变量的值-1
tip:
- ++、-- 在变量前,先运算后参与操作
- ++、-- 在变量后,先参与操作后运算
// 单独使用,输出11
int num1 = 10;
i++;
++i
int num2 = 10;
int num3 = num2++;// 先赋值后运算,num3输出10,num2输出11
int num4 = ++num2;// 先运算后赋值,num4、num2均为11
关系运算符
==、!=、>、>=、<、<=
// 关系运算的结果为boolean类型
逻辑运算符
&
&&(有短路效果,左边为假,右边不执行)
|
||(有短路效果,左边为真,右边不执行)
!(非)
^(逻辑异或:a^b,a和b结果不同为true,相同为false)
位运算
&
|
^
<<
>>
三元运算符
关系表达式 ? 表达式1 : 表达式2;
流程控制语句
分支语句if
if (关系表达式) {
条件为true时执行的代码
} else if (关系表达式) {
条件为true时执行的代码
} else {
条件都不满足时执行的代码
}
switch语句
switch (表达式){
case 值:
匹配该值,执行此处代码;
break;
......
default:
都不匹配,执行此处代码;
break;
}
for循环
for (初始化语句;条件判断语句;条件控制语句) {
循环体语句;
}
while循环
初始化语句;
while (条件判断语句) {
循环体语句;
条件控制语句
}
do…while循环
初始化语句;
do {
循环体语句;
条件控制语句;
} while (条件控制语句);
三种循环的区别
- for循环和while循环先判断条件是否成立,然后决定是否执行循环体**(先判断后执行)**
- do…while循环先执行一次循环体,再判断条件是否成立,是否继续执行循环体**(先执行后判断)**
- for循环的自增变量,因归属for循环的语法内,循环结束后不可被访问
- while,do…while循环的自增变量,不归属其语法中,循环结束后可继续访问
死循环(无限循环)的三种格式:
for (;;){
}
while(true){
}
do {
}while(true);
跳转控制语句
- 跳转控制语句(break)
- 跳出循环,结束循环
- 跳转控制语句(continue)
- 跳过本次循环,继续下次循环
tip:continue只能在循环中进行使用!
流程控制语句可相互嵌套
数组
定义数组
// 第1种:数据类型[] 数组名;
int[] arr;
double[] arr;
char[] arr;
// 第2种(不推荐):数据类型 数组名[];
int arr[];
double arr[];
char arr[];
动态初始化
// 数据类型[] 数组名 = new 数据类型[数组长度];
int[] arr = new int[3];
静态初始化
/*
完整版格式:
数据类型[] 数组名 = new 数据类型[]{元素1,元素2,....};
*/
int[] arr1 = new int[]{
1,2,3};
/*
简化版格式:
数据类型[] 数组名 = {元素1,元素2,...};
*/
int[] arr = {
1,2,3};
数组元素访问
//数组名[索引];
arr[0];
tip:勿超出正确的索引范围,不可赋给数组null
/*
获取数组的长度:
数组名.length
*/
arr.length
类和对象
定义类 class
- 类:拥有共同属性和行为特性的一类事物的抽象
/*
一个文件中public类名和文件名一致,且只有一个public类
首字母大写,需符合标识符的命名规则
类中可写:属性、构造方法、方法、代码块
*/
权限 class 类名{
/*
成员变量:
描述状态的 属性可赋值,亦可不赋值(不赋值系统会赋予默认值)
变量的数据类型 变量名;
*/
int sex;
double price = 18;
/* 一般情况下常量这么写 */
final double NUM = 3.14;
/**
* 构造方法:
创建对象时初始化属性(可有很多个,名字和类名一样)
每创建一个对象,构造方法就会被调用一次,初始化该对象的属性
构造方法的重载:一个类中多个构造方法的参数不同(个数不同、类型不同、顺序不同)
* 权限 类名(参数){}
* */
public Demo(){
}
/**
成员方法:描述能力
权限 返回值类型 方法的名称(参数...){方法体}
方法名称首字母小写,遵循标识符的命名规则
* */
public void getSex() {
}
/**
方法的重载:名称一致,参数不同(个数、类型、顺序)
* */
public double eat(double price) {
/**
* 方法的返回值 return
return是调用完方法后返回的结果,后面不可写代码
返回的类型要和方法声明的类型一致
* */
return price;
}
}
对象的使用 new
每new一次,就会创建一个对象
/*
创建对象的格式:
类名 对象名 = new 类名(参数);
*/
Demo demo = new Demo();
/**
调用成员的格式:
对象名.成员变量
对象名.成员方法();
*/
demo.sex = 1;
demo.getSex();
// 调用有返回值的方法,可用同类型变量接收返回值
double price = d4.eat(100);
包 package
防止命名冲突,访问控制,提供搜索和定位类、接口等
/*
package(包类似于文件夹,包名必须放在文件的第一行)
package 文件路径;
package pkg1[.pkg2[.pkg3…]];
*/
package net.java.util;
/*
引入包语法:使用的类和当前类不在同一个包中,需导包;(new 对象的时候会自动导入)
import 文件路径;
import package1[.package2…].(classname|*);
*/
import java.util.Scanner;
访问权限
Java中 属性、方法都是用权限修饰的(由小到大)
- private:私有的,只能在当前类中使用
- default:默认,同包访问
- protected:继承,子类访问
- public:公共,跨包访问
tips:大多数情况下属性是private声明的,保证属性的私密性; 要操作属性可给它提供操作属性的方法。
单例模式
/**
单例模式:保证一个类只有一个对象(以下3个方法为一组)
同一包下调用:类名 变量名 = 类名.返回私有对象的公有方法名即如下的getInstance方法
不能new对象了,只能如上调用
* */
private static DemoDay5 d5 = new DemoDay5();// 创建私有属性 返回私有对象
private DemoDay5() {
/*构造方法私有化*/}
public static DemoDay5 getInstance() {
return d5;}
继承 extends
-
Java中的继承是类的继承 单继承 使用extends关键字
-
子类可继承父类的属性和方法
-
Java 支持多重继承
继承和权限
private权限的属性和方法不可被继承
default 属性和方法同包可继承
protected 属性和方法可跨包继承 但跨包后只能在子类的内部使用
public 属性和方法可跨包继承和访问
继承中的构造方法
构造方法不被继承
在创建子类对象的时候调用父类的构造方法 初始化父类的属性
默认调用父类的默认无参的构造方法
若父类无默认无参的构造方法,在子类的构造方法中需调用父类的有参的构造方法来初始化父类的属性
调用父类的构造方法需在子类构造方法中
super关键字
调用父类被隐藏的属性、方法
子类有和父类相同的属性、方法,使用super.调用
方法的重写
子类重写父类的方法:
权限不能缩小 返回值类型一致 名称一致 参数相同(类型、个数、顺序)
重写可用==@override
==注解来检查重写是否正确
继承和多态
概念:不同的对象调用同一个方法产生不同的响应
实现的必要条件:
- 子类继承父类或实现接口
- 子类重写父类或接口的方法
- 父类声明子类或接口对象
tip:父类声明子类的对象 该对象只能调用父类中继承的属性和方法(Java的早期绑定)
若想调用子类独有的属性和方法 那么必须将这个对象强制转换成子类的类型
继承中的static
属性共用
方法能继承但是不能被重写
组织继承类使用final声明
final声明的类 不可被继承
public final class Father {
}
final声明的属性是常量,可被继承,但不可被重写
final声明的方法可被继承,但是不可被重写
object(是所有的类的父类【超类】)
一个类可以继承类的同时再实现接口,继承类要写在实现接口之前
初始化和清理
初始化
保证程序的安全执行
属性:先声明,再至上而下赋值
初始化的顺序:
类的初始化:.class文件加载到JVM到过程
类的初始化只进行一次
对象的初始化:针对非静态属性和方法,每创建一次对象就初始化一次
顺序:
父类静态属性、静态块、方法、子类静态属性、方法、父类非静态属性、方法、构造方法
先静态后非静态
先声明后赋值
先属性后方法
清理
如何判断哪些对象要清理:
计数(主流JVM不用)、可达
抽象类 abstract
权限 abstract 类名 {
}
抽象类中可有属性、方法、构造方法(单继承
抽象方法只能写在抽象类中用abstract声明,没方法体。
抽象类的子类中重写抽象方法
抽象类可实现多个接口,实现接口后可不重写接口中的抽象方法,在继承该抽象类的具体类中重写全部抽象方法
接口 interface
接口是一种约束和规范
public interface 接口{
}
// 实现接口
implements 接口名
-
属性是public static final声明的常量(JDK8后可省略
-
JDK8之前都是没有方法体的方法;8之后可写方法体,需用default声明
-
没有构造方法
-
可实现多态
-
多继承(一个接口可继承多个接口)
实现接口的子类中重写接口的抽象方法,一个类可实现多个接口
实现过的接口都可声明该类的对象(接口声明早期绑定的都是接口中的属性和方法,其他的访问不到,若访问需类型强转)
内部类
在类中定义一个类
分类:
- 内部类
- 静态内部类
- 局部内部类
- 匿名内部类
异常 Exception
程序出现了不正常情况
Java中的异常:
体系结构:
- 所有类的父类Throwable类
- Error
- Exception
- 编译器异常:继承Exception(在编译时一定要处理的异常)
- 运行期异常,Runtime Exception也是Exception的子类但是这个类是运行期异常(可做处理亦可不做处理的异常))
处理异常的方式:
-
Throws
将异常抛给调用这个方法的方法
-
调用这个方法的时候不能抛出比这个方法抛出的类小(抛出这个类型或是该类的父类)
-
Throws抛出多个异常的时候可,隔开或者抛出他们的父类
-
JVM处理的throws的异常(main方法throws)发生异常程序终止运行
-
-
try catch
-
try的{}中是可能出现异常的代码
-
catch()中是产生异常时JVM给的异常对象
-
catch{}中写的处理这个异常的代码 处理程序可以继续执行下去;
-
catch可以写(从小到大)多个catch异常父类在最后 匹配(捕获 )一个异常后其他的catch也不执行了
-
finally{}不管异常与否 都会执行的代码块
-
自定义异常
-
编译期异常
- 创建一个类 继承Exception
-
运行期异常
- 创建一个类 继承Runtime Exception
需要的时候throws new 异常 对象即可
tips:
继承关系中父类方法没抛出异常 子类重写这个方法也不能抛出异常 只能try catch处理掉
常用API
String类
StringBuilder
正则表达式“”
// java中的正则写在""
// Java中的正则表达式可在String的matches方法中使用
boolean f = "aa".matches("正则表达式"); //matches方法中写正则表达式
字母和数字范围[]
- [a-z] 字母a-z
- [A-Z] 字母A-Z
- [0-9] 数字0-9
String str = "a";
boolean f = str.matches("[a-z]"); // 匹配字母a-z
f = str.matches("[A-Z]"); // 匹配字母A-Z
f = str.matches("[0-7]"); // 匹配数字0-7
f = str.matches("[0-9a-zA-Z]");// 数值、字母都匹配
预定义类(元字符)
- \d 数值
- \D 非数值
- \w 数字、字母、下划线
- \W 非数字、字母、下划线
- \s 空白符
- \S 非空白符
String str="";
boolean f = str.matches("\\d"); // 匹配数值【0-9】
f = str.matches("\\D"); // 匹配非数值
f = str.matches("\\w"); // 匹配数字字母下划线
f = str.matches("\\W"); // 匹配非数字字母下划线
f = str.matches("\\s"); // 匹配空白符
f = str.matches("\\S"); // 匹配非空白符
个数的匹配
-
{m} m个
-
{m,n} m~n个
-
{m,} 至少m个
-
*号 0~n个
-
? 0或1个
-
+号 1个以上
String str = "13900998877";
f = str.matches("1[3589][0-9]{9}");// 匹配手机号
任何符号的匹配 .
String str = "xxiix";
boolean f = str.matches(".+");// 匹配任何符号1个以上
转义字符 \
String str = ".";
boolean f = str.matches("\\.");// 匹配.
中文符号的匹配 [\u4E00-\u9FA5]
String str = "中文";
// 匹配中文2~4个
boolean f = str.matches("[\\u4E00-\\u9FA5]{2,4}")
分组的匹配 ()
String str = "521521521";
boolean f = str.matches("(521)+"); // 匹配1组以上521
边界匹配 ^ $
- ^ 开始
- $ 结束
String str = "ceshi88";
boolean f = str.matches("^[a-z]+\\d+$");
^写在[]中表示非
String str = "zhongwen";
boolean f = str.matches("[^0-9]*") // 匹配0~9以外字符
Pattern类
//Pattern类是Java中内置的正则
Pattern p=Pattern.compile("\\d{6}");
//要验证的字符串
Matcher m=p.matcher("99ee9888");
//开始比较字符是否符合规则
boolean flag=m.matches();
System.out.println(flag);
Pattern p = Pattern.compile("cat");
Matcher m = p.matcher("one cat two cats in the yard");
StringBuffer sb = new StringBuffer();
while (m.find()){
m.appendReplacement(sb, "dog");
}
m.appendTail(sb);
System.out.println(sb.toString()); //one dog two dogs in the yard
集合
- 单列:Conllection
- 可重复:List
- ArrayList
- LinkedList
- …
- 不可重复:Set
- HashSet
- TreeSet
- …
- 可重复:List
- 双列:Map
- HashMap
Collection集合
Collection集合概述:
- 是单例集合的顶层接口,表示一组对象,这些对象也称为Collection的元素
- JDK不提供此接口的任何直接实现,它提供更具体的子接口(Set和List)实现
创建Collection集合的对象:
- 多态的方式
- 具体的实现ArrayList
/*
创建Collection集合的对象
多态的方式:ArrayList()
*/
Collection<String> c = new ArrayList<>();
// 添加元素:boolean add(E e);
c.add("hello");
c.add("word");
// 从集合中移除指定元素:boolean remove(E e);
c.remove("word");
// 清空集合中的元素:void clear();
c.clear();
// 判断集合中是否存在指定元素:boolean contoins(Object o);
System.out.println(c.contains("hello"));
// 判断是否为空:boolean isEmpty();
System.out.println(c.isEmpty());
// 获取集合长度:int size();
System.out.println(c.size());
遍历 Iterator
Iterator:迭代器,集合的专用遍历方式
- Iterator iterator():返回此集合中元素的迭代器,通过集合的iterator()方法得到
- 迭代器是通过集合的iterator()方法得到的,所以说它是依赖于集合而存在的
Collection<String> c = new ArrayList<>();
// 获取集合的迭代器
Iterator<String> it = c.iterator();
/*** Iterator中的常用方法:***/
// 获取迭代器中下一个元素:E next()
System.out.println(it.next);
// 判断是否有更多元素:boolean hasNext()
while (it.hasNext()) {
System.out.println(it.next());
}
List集合
- 有索引
- 有序集合:存储和取出的元素顺序一致
- 可重复
// 创建List集合对象
List<String> list = new ArrayList<>();
// 添加元素
list.add("hello");
// 在指定位置插入元素:void add(int index, E element);
list.add(1,"java");
// 删除指定索引的元素,返回被删除的元素:E remove(int index, E element);
list.remove(1);
// 修改指定索引的元素,返回被修改的元素:E set(int index, E element);
list.set(1,"word");
// 返回指定索引的元素:E get(int index);
System.out.println(list.get(1));
// 迭代器方式循环:
Iterator<String> it = list.iterator();
while(it.hasNext()) {
System.out.println(it.next());
}
// for循环方式:
for (int i=0; i<list.size(); i++) {
System.out.println(list[i]);
}
并发修改异常
/**
* 并发修改异常 ConcurrentModificationException
* 产生原因:迭代器遍历的过程中,通过集合对象修改了集合中元素的长度,造成了迭代器获取元素判断预期修改值和笔记修改值不一致
* 解决方案:用for循环遍历,然后用集合对象做对应的操作即可
* */
List<String> list2 = new ArrayList<>();
list2.add("hello");
list2.add("word");
list2.add("java");
// Iterator<String> it = list2.iterator();
// while (it.hasNext()) {
// if (it.next().equals("word")) {
// list2.add("javaee");
// }
// }
for (int i=0; i<list2.size(); i++) {
if (list2.get(i) == "word") {
list2.add("javaee");
}
}
列表迭代器ListIterator
- 通过list集合的listIterator()方法得到,是list集合特有的迭代器
- 可沿任一方向遍历列表的迭代器,在迭代期间修改列表,并获取列表中迭代器的当前位置
ListIterator中的常用方法:
List<String> list = new ArrayList<>();
list.add("hello");
list.add("word");
// 获取列表迭代器
ListIterator<String> lit = list.listIterator();
// E next():返回迭代中的下一个元素
lit.next();
// boolean hasNext():判断迭代是否有更多元素
lit.hasNext();
// E previous:返回迭代中的上一个元素
lit.previous();
// boolean hasPrevious():判断迭代在相反方向是否有更多元素
lit.hasPrevious();
// void add(E e):将指定元素插入列表【重点】
while(lit.hasNext()) {
if (lit.next().equals("word")) {
lit.add("正向循环:ListIterator-add");
}
}
while(lit.hasPrevious()) {
if (lit.previous().equals("word")) {
lit.add("逆向循环:ListIterator-add")
}
}
增强for循环
简化数组和Collection集合的遍历
- 实现Iterator接口的类允许其对象成为增强型 for语句的目标
- JDK5之后出现的,内部原理:一个Iterator迭代器
/*
for(元素数据类型 变量名:数组或Collection集合对象名) {
// 循环体【在此处使用变量即可,该变量即为元素】
}
*/
int[] arr = {
1,2,3,4,5};
for (int i:arr) {
System.out.println(i);
}
遍历循环
- 迭代器:集合特有的遍历方式
- 普通for:带有索引的遍历方式
- 增强for:最方便的遍历方式
ArrayList<Student> students = new ArrayList<>();
addStu(students);
addStu(students);
addStu(students);
// 迭代器:集合特有的遍历方式
Iterator<Student> it = students.iterator();
while (it.hasNext()) {
Student next = it.next();
System.out.println(next.name + next.age);
}
// 普通for:带有索引的遍历方式
for (int i=0; i<students.size(); i++) {
System.out.println(students.get(i).name + students.get(i).age);
}
// 增强for:最方便的遍历方式
for (Student stu:students) {
System.out.println(stu.name + stu.age);
}
List集合的实现类
List集合常用子类:ArrayList、LinkedList
- ArrayList:底层数据结构是数组,查询快,增删慢
- LinkedList:底层数据结构是链表,查询慢,增删快
数据结构
栈和队列
- 栈结构:先进后出(进栈、出栈)
- 队列结构:先进先出(入队列、出队列)
数组和链表
- 数组结构:查询快,增删慢(查询效率高,删除效率低,添加效率极低)
- 链表结构:查询慢,增删快【相对数组而言】
LinkedList
// 创建LinkedList集合对象
LinkedList<String> linkedList = new LinkedList<>();
// 添加元素
linkedList.add("支支吾吾");
linkedList.add("fansi");
// 三种循环方式
Iterator<String> it4 = linkedList.iterator();
while (it4.hasNext()) {
System.out.println(it4.next());
}
for (int i=0; i<linkedList.size(); i++) {
System.out.println(linkedList.get(i));
}
for (String str:linkedList) {
System.out.println(str);
}
LinkedList特有功能
- 特有方法:
方法名 | 说明 |
---|---|
public void add First(E e) | 在该列表开头插入指定元素 |
public void add Last(E e) | 将指定的元素追加到此列表的末尾 |
public E getFirst() | 返回该列表的第一个元素 |
publi E getLast() | 返回该列表最后一个元素 |
public E removeFirst() | 删除此列表第一个元素,并返回该元素 |
public E removeLast() | 删除此列表最后一个元素,并返回该元素 |
// 创建LinkedList集合对象
LinkedList<String> linkedList1 = new LinkedList<>();
linkedList1.add("linkedlist-add1");
linkedList1.add("linkedlist-add2");
linkedList1.add("linkedlist-add3");
// public void add First(E e) | 在该列表开头插入指定元素
linkedList1.addFirst("first");
// public void add Last(E e) | 将指定的元素追加到此列表的末尾
linkedList1.addLast("last和add方法一样的作用");
// public E getFirst() | 返回该列表的第一个元素
System.out.println(linkedList1.getFirst());
// publi E getLast() | 返回该列表最后一个元素
System.out.println(linkedList1.getLast());
// public E removeFirst() | 删除此列表第一个元素,并返回该元素
System.out.println(linkedList1.removeFirst());
// public E removeLast() | 删除此列表最后一个元素,并返回该元素
System.out.println(linkedList1.removeLast());
ArrayList
ArrayList:
- 可调整大小的数组实现
- :是一种特殊的数据类型,泛型
/*
* ArrayList构造方法:
public ArrayList();// 创建一个空集合对象
*/
ArrayList<String> arr = new ArrayList<>();// 1.7新特性
ArrayList<String> arr = new ArrayList<String>();// 1.7前的写法
集合常用方法
/*
public boolean add(E e); 将指定元素追加到此集合的末尾
public void add(int index,E element); 在此集合中的指定位置插入指定元素
public boolean remove(Obiect o); 删除指定元素,返回是否成功
public E remove(int index); 删除指定索引的元素,返回被删除的元素
pubic E set(int index,E element); 修改指定索引的元素,返回被修改的元素
public E get(int index); 返回指定索引的元素
public int size(); 返回集合中元素的个数
*/
arr.add("hello");// 将指定元素追加到此集合的末尾
Set集合
- 元素存取无序
- 不包含重复元素的集合
- 没有索引,不可用普通for循环遍历
// 创建集合对象
Set<String> set = new HashSet<>();
// 添加元素,不能存储重复元素
set.add("hello");
set.add("word");
set.add("java");
// 遍历
for (String i:set) {
System.out.println(i);
}
哈希值
JDK根据对象的地址或字符串或数字算出来的int类型的数值
Object类中有一个方法可获取对象的哈希值
- public hashCode():返回对象的哈希码值
Student student = new Student();
// 同一个对象多次调用hashCode()方法返回的哈希值是相同的
System.out.println(student.hashCode());
System.out.println(student.hashCode());
Student s2 = new Student();
// 默认情况下,不同对象的哈希值是不同的(通过方法重写,可实现不同对象的哈希值相同)
System.out.println(s2.hashCode());
// 字符串重写了hashCode()方法,所以同样字符串哈希值值一样
System.out.println("hello".hashCode());// 99162322
System.out.println("word".hashCode());// 3655434
System.out.println("word".hashCode());// 3655434
// 特例:值一样
System.out.println("重地".hashCode());
System.out.println("通话".hashCode());
数据结构之哈希表
- JDK8之前,底层采用数组+链表实现,即为一个元素为链表的数组
- HashSet()方法默认初识容量为16
- JDK8以后,在长度比较时,底层实现了优化
HashSet
- 底层数据结构是哈希表(散列表)
- 对集合的迭代顺序不做任何保证,不保证存储和取出的元素顺序一致
- 没有索引的方法,不能使用普通for循环遍历
- 由于是Set集合,不包含重复元素的集合
- 调用hashCode()方法来判断哈希值是否相同,相同调用equals()方法判断内容是否一致,一致则不存储
- 要保证元素的唯一性,需重写hashCode()和equals()
// 创建集合对象
HashSet<String> hashSet = new HashSet<>();
// 添加元素
hashSet.add("hello");
hashSet.add("word");
hashSet.add("java");
hashSet.add("java");// 不包含重复元素,所以不会被存储进去
// 遍历
for (String s:hashSet) {
System.out.println(s);
}
LinkedHashSet
- 哈希表和链表实现的Set接口,具有可预测的迭代次序
- 由链表保证元素有序,即元素的存储和取出顺序是一致的
- 由哈希表保证元素唯一,即没有重复的元素
- 没有索引
// 创建LinkedHahSet集合对象
LinkedHashSet<String> objects = new LinkedHashSet<>();
// 添加元素
objects.add("LinkedHashSet");
objects.add("hello");
objects.add("java");
objects.add("java");
// 遍历
for (String s:objects) {
System.out.println(s);
}
TreeSet
- 元素有序,这里的顺序不是指存储和取出的顺序,而是按照一定的规律进行排序,具体排序方式取决于构造方法
- TreeSet():根据元素的自然排序进行排序
- TreeSet(Comparator comparator):根据指定的比较器进行排序
- 没有带索引的方法,不可使用普通for循环遍历
- 由于是Set集合,所以不包含重复元素的集合
// 创建集合对象
TreeSet<Integer> integers = new TreeSet<>();
// 添加元素
integers.add(10);
integers.add(50);
integers.add(30);
integers.add(20);
integers.add(20);// 不包含重复元素,所以不存储
// 遍历
for (Integer i:integers) {
System.out.println(i);
}
自然排序Comparable的使用
- 用TreeSet集合存储自定义对象,无参构造方法使用的是自然排序对元素进行排序的
- 自然排序,就是让元素所属的类实现Comparable接口,重写compareTo(To)方法
- 重写方法时,一定要注意排序规则必须按照要求的主要条件和次要条件来写
// 实现Comparable接口 <E>:泛型
implements Comparable<Student>
@Override
public int compareTo(Student s) {
// return 0;// 等于上一个 重复的不添加
// return 1;// 大于上一个 正序
// return -1;// 小于上一个 倒序
// 按照年龄正序 this.age当前年龄 s.age上一个年龄
int num = this.age-s.age;
// 年龄相同按姓名排序
int num2 = num == 0 ? this.name.compareTo(s.name) : num;
return num2;
}
比较器排序Cmparator的使用
- 用TreeSet集合存储自定义对象,带参构造方法使用的是比较排序对元素进行排序的
- 比较排序,即让集合构造方法接受Comparator的实现类对象,重写compare(T o1, T o2)方法
- 重写方法是,一定要注意排序规则必须按照要求的主要条件和次要条件来写
// 创建TreeSet集合对象
TreeSet<Student> students = new TreeSet<>(new Comparator<Student>() {
@Override
public int compare(Student o1, Student o2) {
// 按照年龄正序 this.age当前年龄 s.age上一个年龄
int num = o1.age-o2.age;
// 年龄相同按姓名排序
int num2 = num == 0 ? o1.name.compareTo(o2.name) : num;
return num2;
}
});
Student s1 = new Student("a", (byte) 18);
Student s2 = new Student("b", (byte) 20);
Student s3 = new Student("c", (byte) 18);
Student s5 = new Student("d", (byte) 19);
Student s6 = new Student("d", (byte) 19);
// 添加元素
students.add(s1);
students.add(s2);
students.add(s3);
students.add(s5);
// 遍历
for (Student student:students) {
System.out.println(student.name + student.age);
}
Map
interface Map<K,V> K:键的类型;V:值的类型
Map集合的特点:
- 键值对映射关系(双列)
- 一个键对应一个值
- 键不可重复,值可重复
- 元素存取无序
示例代码:
// 创建集合对象
Map<String,String> map = new HashMap<>();
// V put(K key, V value) 将指定的值与该映射中的指定键相关联
map.put(