JAVA基础
一、开发环境
JDK:JRE+开发工具
JRE:JVM+核心类库
二、变量及数据类型
1.注释
单行注释://注释信息
多行注释:/* 注释信息 */
文档注释:/** 注释信息* */
2.关键字
特点:关键字的字母全部用小写。
3.数据类型
三、常用API
String
-
特点
- 创建之后不可修改
- 可共享
- 底层实现采用字节数组(Byte[])
-
常用构造方法
方法名 描述 public String() 无参构造方法,不含任何内容 public String(char [] chs) 根据字符数组内容,创建字符串对象 public String(byte [] bys) 根据字节数组内容,创建字符串对象 String s = “abc” 直接赋值,内容为abc -
常用操作
方法名 描述 equals() 比较两个对象的值是否相同 == 基本数据类型:比较值;引用数据类型:比较对象地址 charAt(int index) 获取字符串中的单个字符,默认从0开始 length() 获取字符串长度 + 拼接字符串
StringBuilder
-
特点
- 字符串可变类,可看做容器
-
常用构造方法
方法名 描述 public StringBulider() 创建空白字符串,不含任何内容 public StringBuilder(String str) 根据字符串内容创建可变字符串 -
常用操作
方法名 描述 public StringBulider append() 添加数据并返回对象本身 public StringBulider reverse() 反转字符串序列
String 和 StringBulider相互转化
- String -> StringBulider: public StringBulider(String str)
StringBuilder -> String: public String toString()
ArrayList
-
特点
- 可调整大小的数组
- 格式:ArrayList< E > 数组名 = new ArrayList< E >()
- < E > 作为一种特殊的数据类型,泛型
-
常用操作
方法名 描述 public boolean add(E element) 向数组中追加字符串至末尾 public boolean remove(Object o) 删除指定元素,返回删除结果 public E remove(int index) 删除指定索引处的元素,并返回删除元素 public E set(int index,E element) 修改指定所引出的元素,返回被修改元素 public E get(int index) 返回指定所引出的元素 public int size() 返回结合中元素的个数
四、继承
五、抽象类
-
定义:
public abstract class 类名{...}
-
特点
- 含抽象方法的类必须被定义为抽象类
- 抽象类中可以含抽象方法,也可以含具体方法
- 抽象类创建对象采用多态方式实现
- 子类继承抽象父类,必须对父类中的所有抽象方法进行重写
- 如果不想对继承的抽象方法进行重写,则必须将类定义为抽象类
-
案例
//Animal类 public abstract class Animal { public abstract void eat(); } //Cat类 public class Cat extends Animal{ @Override //必须重写抽象类中的所有抽象方法 public void eat() { System.out.println("猫吃鱼"); } } //Test类 public class AnimalDemo { public static void main(String[] args) { Animal a = new Cat(); //抽象类实例化要通过多态方式 a.eat(); } }
六、接口
-
定义
-
一种公共的规范标准,是对公共行为的抽象
-
public interface 接口名{ public abstract 返回值类型 方法名(); //... }
- ```java //类实现接口 public class 类名 implements 接口名{ //重写接口中所有抽象方法 }
-
-
特点
- 使用关键字Interface定义
- 接口中的成员变量默认final(不可修改)、static(可通过类名直接调用)
- 接口中不能有构造方法,一个类没有父类,默认继承自Object类
- 接口中只能有抽象方法
- 类和接口之间不能用extends,要使用implements(实现)
- 类实现接口必须重写接口中的所有抽象方法
- 接口的实例化采用多态方式
- 抽象类可以实现接口,接口+抽象类可以实现多继承
-
类和接口的关系
- 类和类:只能单继承,可以多层继承
- 类和接口:类实现接口,可以多实现,也可以继承一个类实现多个接口
- 接口和接口:可以单继承也可以多继承
-
案例
//Animal接口: public interface Animal{ public abstract void jump(); } //Cat类实现Animal接口 public class Cat implements Animal{ @override public void jump(){ System.out.println("猫跳高"); } } //Test类: public class Test{ public static void main(String[] args){ Animal a = new Cat(); //多态方式实例化 a.jump(); //调用Cat重写方法 } }
七、内部类
-
定义
-
就是在一个类中定义一个类,通常是为了隐藏
-
格式:
public class 类名{ 修饰符 class 类名{ } }
-
-
分类
-
在类的成员位置:成员内部类(一般来说不常见)
//创建内部类对象 //外部类名.内部类名 对象名 = 外部类对象.内部类对象; //例:Outer.Inner oi = new Outer().new Inner();
-
在类的局部位置:局部内部类
//在方法里面定义内部类 //例: public class Outer{ private int num = 10; public void method(){ class Inner{ //局部内部类 public void show(){ System.out.println(num); } } Inner i = new Inner(); //不创建对象,method方法则无法访问Inner i.show(); } } //访问局部内部类 Outer o = new Outer(); o.method(); //输出10
-
匿名内部类(局部内部类的一种)
//前提:存在一个类或者抽象类,这里的类可以具体也可以抽象 //本质:是一个继承了该类或者实现了该接口的子类匿名对象 //格式: new 类名或者接口名(){ //重写方法 }; //示例: public interface Inter{ void show(); } public class Outer{ public void method(){ new Inter(){ @override public void show(){ System.out.println("匿名内部类"); } }.show(); //匿名对象调用内部类方法 } } //测试类 public class Test{ public static void main(){ Outer i = new Outer(); i.method; //输出:匿名内部类 } }
-
-
特点
- 内部类可以直接访问外部类的成员,包括私有
- 外部类要想访问内部类的成员,必须要创建对象
-
案例
public class Outer{ private int num = 10; public class Inner{ public void show(){ System.out.println(num); } } public void method(){ //show() 直接调用报错,必须通过创建对象调用 Inner i = new Inner(); i.show(); } }
八、工具类
Math类
- 包:java.lang.Math
- 常用方法
方法名 | 描述 |
---|---|
public static E abs(E e) | 求绝对值,通过类名直接调用 |
public static double floor(double a) | 返回小于或等于参数的最小double 值,并等于某个整数 |
public static int round(float a) | 按照四舍五入返回最近参数的int |
public static int max(int a,int b) | 返回a,b中的较大值 |
public static double ceil(double a) | 返回大于或等于参数的最小double 值,并等于某个整数。 |
public static int min(int a,int b) | 返回a,b中的较小值 |
public static double pow(double a,double b) | 返回a的b次方 |
public static double random() | 返回值为double类型,范围[0.0,1.0) |
System类
- 包:java.lang.System
- 常用方法
方法名 | 描述 |
---|---|
public static long currentTimeMillis() | 返回当前时间(毫秒为单位),从1970年1月1日开始算起 |
public static void exit(int status) | 终止当前运行的java虚拟机,非零表示异常终止 |
Object类
- 包:java.lang.Object(Object类是所有类的父类)
- 常用方法
方法名 | 描述 |
---|---|
public String toString() | 返回对象的字符串表示形式。重写自动生成。底层实现:getClass().getName()+’@’+Integer.toHexString(hashCode()) |
public boolean equals(Object obj) | 比较两个对象是否相等(地址值),比较数值需要进行重写,自动生成 |
public int hashCode() | 返回对象的哈希码值 |
Arrays类
-
冒泡排序算法
package com.itheima_02; public class Array { public static void main(String[] args) { //定义数组 int[] arr = {24, 69, 80, 57, 13}; System.out.print("排序前:" + arrayToString(arr) + '\n'); System.out.print("排序后:" + arrayToString(bubbleSort(arr))); } //将数组按照一定规则组合成字符串 public static String arrayToString(int[] arr){ StringBuilder sb = new StringBuilder(); sb.append("["); for(int i = 0; i < arr.length; i++){ if(i == arr.length - 1) sb.append(arr[i]); else{ sb.append(arr[i]).append(", "); } } sb.append("]"); String s = sb.toString(); return s; } //冒泡排序算法 public static int[] bubbleSort(int[] arr){ for(int i = 1; i <= arr.length-1; i++){ for(int j = arr.length-1; j >= 1; j--){ if(arr[j] < arr[j - 1]){ int temp = arr[j]; arr[j] = arr[j - 1]; arr[j - 1] = temp; } } } return arr; } } //输出结果 /*排序前:[24, 69, 80, 57, 13] 排序后:[13, 24, 57, 69, 80]*/
-
包:java.util.Arrays
-
常用方法
方法名 | 描述 |
---|---|
public static String toString(int[] a) | 返回指定数组的内容的字符串表示 |
public static void sort(int[] a) | 按照数组顺序排列指定的数组 |
基本类型包装类
-
Integer
-
常用方法:
方法名 描述 public static Integer valueOf(int i) 返回表示指定的int值的Integer实例 public static Integer valueOf(String s) 返回一个保存指定值的Integer对象 -
int与String的相互转换
int------->String: public static String valueOf(int i)
//方式一: int number = 100; String s1 = "" + number; System.out.println(s1) //方式二: String s2 = String.valueOf(number);
String------>int: public static int **parseInt(**String s)
//String----->int String s = "100"; //方式一: Integer i = Integer.valueOf(s);//将String变为Integer int number = i.intValue(); //将Integer变为int //方式二: int number = Integer.parseInt(s);
-
-
Short,Byte,Double,Float,Character,Boolean,Long
-
案例练习
//将一个字符串“91 27 46 38 50”转变为“27 38 46 50 91” import java.util.Arrays; public class StringSort { public static void main(String[] args) { /* 实现步骤: 1、定义一个字符串 2、把字符串中数据存储到int类型数组中,使用split()方法 3.调用sort()方法进行排序 4、将排序后的数组转换为字符串,使用StringBuilder实现 */ String s = "91 27 46 38 50"; String[] strArray = s.split(" "); int[] arr = new int[strArray.length]; for(int i = 0; i < strArray.length; i++) arr[i] = Integer.parseInt(strArray[i]); Arrays.sort(arr); StringBuilder sb = new StringBuilder(); for(int i = 0; i < arr.length; i++){ if(i == arr.length-1) sb.append(arr[i]); else sb.append(arr[i]).append(" "); } System.out.println(sb.toString()); } }
日期类
-
Date
- 包:java.util.Date
- 常用方法
方法名 描述 public long getTime() 获取的是日期对象从1970年1月1日00:00:00到现在的毫秒值 public void setTime(long time) 设置时间,给的是毫秒值 -
SimpleDateFormat类
- 包:java.text.SimpleDateFormat
- 作用:允许用户自行定义日期显示的格式
- 构造方法
构造方法 描述 public SimpleDateFormat() 构造一个SimpleDateFormat,使用默认的日期格式 public SimpleDateFormat(String pattern) 构造一个SimpleDateFormat,使用给定的模式和默认的日期格式 - 格式化:public final String format(Date date) 将日期格式化为日期/日期字符串
- 解析:public Date parse(String source): 从给定字符串的开始解析文本以生成日期
import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; public class DateDemo { public static void main(String[] args) throws ParseException { //格式化:从Date到String Date d = new Date(); // SimpleDateFormat sdf = new SimpleDateFormat(); SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss"); String s = sdf.format(d); System.out.println(s); //解析:从string到Date String date = "2022年04月14日 17:09:58"; SimpleDateFormat sdf2 = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss"); Date d2 = sdf2.parse(date); System.out.println(d2); } }
-
Calendar类
- 包:java.util.Calendar
- 抽象类
- 示例
//获取对象 Calendar c = Calendar.getInstance(); int year = c.get(Calendar.YEAR); int month = c.get(Calendar.MONTH) + 1; //月份从0开始,故要+1 int day = s.get(Calendar.Date);
- 常用方法
方法名 描述 public int get(int field) 返回给定日历字段的值 public abstract void add(int field,int amount) 根据日历的规则,将指定的时间量添加或减去给定日历的字段 public final void set(int year,int month,int date) 设置当前日历的年月日
九、异常
-
描述:Throwable类是所有错误和异常的超类
-
两种处理方式:
- try…catch…
- throws 异常类名;
- 二者区别:如果异常代码后面还有要执行的代码,try…catch…处理后可以继续执行,而throws仅做出抛出异常,不会再执行接下来的代码
-
自定义异常
- 格式:
public class 异常类名 extends Exception{ 无参构造; 带参构造; }
- 示例
public class ScoreException extends Exception{ public ScoreException(){}; public ScoreException(String message){ super(message); } }
-
throw和throws区别
throw | throws |
---|---|
用在方法体内,跟的是异常对象名 | 用在方法声明后,跟的是异常类名 |
表示抛出异常,由方法体内语句处理 | 表示抛出异常,由该方法的调用者处理 |
执行throw一定抛出了某种异常 | 表示出现异常的可能性,并不一定发生 |
十、集合类
提供一种存储空间可变的存储模型,存储容量可以随时发生变化
Collection
-
包:java.util.Collection
-
描述:是单例集合的顶层接口,它表示一组对象,这些对象也称为Collection的元素,JDK不提供此接口的任何直接实现,它提供更具体的子接口(如Set和List)实现
-
创建:多态方式;具体的实现类ArrayList
-
常用方法
方法名 描述 boolean add(E e) 添加元素,永远返回true void clear() 从此集合中删除所有元素 boolean isEmpty() 不包含任何元素,返回true int size() 集合中的元素数 boolean remove(Object o) 从集合中移除指定的元素 boolean contains(Object o) 判断集合中是否存在指定的元素 -
遍历
-
使用Iterator迭代器进行遍历
-
iterator方法
- boolean hasNext(): 如果还有元素,返回true
- E next(): 返回迭代中的下一个元素
-
示例:
import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; public class CollectionDemo { public static void main(String[] args) { //向Collection集合中添加元素并遍历 Collection<String> c = new ArrayList<>(); //多态创建 //添加元素 c.add("Hello"); c.add("World"); c.add("java"); //遍历 Iterator<String> iterator = c.iterator(); while (iterator.hasNext()) System.out.println(iterator.next()); } }
-
List
-
包:java.util.List
-
概述:有序集合(序列),用户可以精确控制列中元素的插入位置,可以通过整数索引访问元素,并搜索列表中的元素;与Set集合不同,列表中允许元素重复。
-
示例:
import java.util.ArrayList; import java.util.Iterator; import java.util.List; public class ListDemo { public static void main(String[] args) { // List<String> list = new List<String>(); List是一个借口,需要通过多态创建对象 List<String> list = new ArrayList<>(); //添加元素 list.add("Hello"); list.add("World"); list.add("java"); //输出列表 System.out.println(list); //重写了toString()方法 //用迭代器遍历 Iterator it = list.iterator(); while(it.hasNext()){ String s = (String) it.next(); System.out.println(s); } } }
-
List特有方法
方法名 描述 void add(int index,E element) 在指定位置处插入元素 E remove(int index) 删除指定位置处元素,并返回被删除元素 E set(int index, E element) 修改指定索引处的元素,返回被修改的元素 E get(int index) 返回指定索引出的元素 -
并发修改异常
- 预期修改的次数 != 实际修改的次数,就会抛出异常
-
List遍历方式
-
使用iterator迭代器(遍历时修改会出现异常)
-
使用特有ListInterator()方法(不会出现并发修改异常):允许程序员沿任一方向遍历列表迭代器,在迭代期间修改列表,并获取列表中迭代器的位置
-
使用for循环
-
增强for循环:底层实现依然是迭代器,故在遍历期间修改列表会发生并发修改异常
-
格式:for(元素类型 变量名:数组或集合){}
-
示例:
int[] arr = {1, 2, 3, 4, 5}; for(int i:arr){ System.out.println(i); }
-
-
-
常见List子类
-
ArrayList:底层数据结构是数组,查询快,增删慢
-
LinkedList:底层数据结构是链表,查询慢,增删快
-
LinkedList特有方法
方法名 描述 public void addFirst(E e) 从头部插入元素 public void addLast(E e) 从尾部插入元素 public E getFirst() 返回链表第一个元素 public E getLast() 返回链表最后一个元素 public E removeFirst() 从链表中删除第一个元素并返回 public E removeLast() 从链表中删除最后一个元素并返回
-
Set
-
特点
- 不包含重复元素
- 没有带索引的方法,所以不能使用普通for循环遍历
-
哈希值
- JDK根据对象的地址或者字符串或者数字算出来的int类型的数值
- public int hashCode() : 返回对象的哈希值
- 同一对象多次调用hashCode()方法返回的哈希值是相同的
- 默认情况下,不同对象的哈希值是不同的,而重写hashCode()方法可以实现让不同对象的哈希值相同
-
HashSet集合
-
Set接口的实现类
-
底层数据结构是哈希表
-
对集合的迭代顺序不做任何保证,也就是说不保证存储和取出元素的顺序一致
-
没有带索引的方法,所以不能使用普通for循环遍历(可以使用迭代器和增强for循环)
-
不包含重复元素
-
示例:
import java.util.HashSet; import java.util.Iterator; public class HashSetDemo { public static void main(String[] args) { //创建HashSet对象 HashSet<String> hs = new HashSet<>(); //添加元素 hs.add("Hello"); hs.add("world"); hs.add("java"); //输出 System.out.println(hs); //[world, java, Hello] //迭代器遍历 Iterator<String> it = hs.iterator(); while(it.hasNext()) System.out.println(it.next()); //增强for循环 for(String s : hs) System.out.println(s); } }
-
哈希表(HashSet)
- JDK8之前,底层采用数组+链表实现,可以说是一个元素为链表的数组
- 示例
import java.util.HashSet;
/*
创建一个学生对象集合,存储学生,如果成员变量相同,看做同一个对象
*/
public class HashSetDemo {
public static void main(String[] args) {
//创建学生对象
Student s1 = new Student("林青霞", 18, "女");
Student s2 = new Student("张曼玉", 20, "女");
Student s3 = new Student("王祖贤", 19, "女");
Student s4 = new Student("王祖贤", 19, "女");
//不允许重复元素,用Set实现
// 需要在Student中重写HashCode()和equals()方法,否则s3,s4都会添加成功
HashSet<Student> hs = new HashSet<>();
//添加元素
hs.add(s1);
hs.add(s2);
hs.add(s3);
hs.add(s4);
//增强for遍历
for(Student s : hs)
System.out.println(s.getName() + ","+s.getSex()+","+s.getAge());
}
}
LinkedHashSet
- 特点
- 哈希表和链表实现Set接口,具有可预测的迭代次序
- 由链表保证元素有序,也就说是元素的存储和取出顺序是一致的
- 由哈希表保证元素唯一,也就是说没有重复元素
- 示例:
import java.util.LinkedHashSet;
public class LinkedHashSetDemo {
public static void main(String[] args) {
LinkedHashSet<String> lhs = new LinkedHashSet<>();
lhs.add("hello");
lhs.add("world");
lhs.add("java");
//遍历
for(String s : lhs)
System.out.println(s); //按序输出hello,world,java
}
}
TreeSet
- 特点
- 包:java.util.TreeSet
- 元素有序,这里顺序不是存储和取出的顺序,而是按照一定规则进行排序,具体排序方式取决于构造方法
- TreeSet():根据元素的自然顺序排序
- TreeSet(Comparator comparator):根据指定的比较器进行排序
- 没有带索引的方法,不能使用普通for循环
- 示例:
//Student类
import java.util.Objects;
public class Student implements Comparable<Student>{
private String name;
private int age;
private String sex;
public Student(){};
public Student(String name, int age, String sex){
this.name = name;
this.age = age;
this.sex = sex;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return age == student.age &&
Objects.equals(name, student.name) &&
Objects.equals(sex, student.sex);
}
@Override
public int hashCode() {
return Objects.hash(name, age, sex);
}
@Override
public int compareTo(Student s) {
// return 0; 返回0认为元素重复
//先按照年龄排序,年龄相同,比较姓名首字母
int num = this.age - s.age; //this在前为升序
int num2 = num == 0 ? this.name.compareTo(s.name): num;
return num2;
}
}
//测试类
import java.util.TreeSet;
public class TreeSetDemo {
public static void main(String[] args) {
// TreeSet<int> ts = new TreeSet<int>(); //<?>存储对象,不能使用int基本数据类型
TreeSet<Integer> ts = new TreeSet<>(); //无参构造
ts.add(18);
ts.add(59);
ts.add(23);
for(Integer i : ts)
System.out.println(i); //18 23 59
//创建学生对象,按照年龄大小进行排序,年龄相同,按照姓名字母顺序排序
//Comparable实现
TreeSet<Student> tss = new TreeSet<>();
Student s1 = new Student("xishi",18,"女");
Student s2 = new Student("diaochan",20,"女");
Student s3 = new Student("yangyuhuan",18,"女");
Student s4 = new Student("wangzhaojun",22,"女");
//添加学生到集合
tss.add(s1);
tss.add(s2);
tss.add(s3);
tss.add(s4);
//遍历 需要Student类实现Comparable接口才能进行排序
for(Student s: tss)
System.out.println(s.getName()+"," + s.getAge() + ","+s.getSex());
}
}
比较器Comparator
- 示例
import java.util.Comparator;
import java.util.TreeSet;
public class ComparatorDemo {
public static void main(String[] args) {
/*
存储学生对象并遍历,创建TreeSet集合使用带参构造方法
要求:按照年龄从小到大排序,年龄相同时,按照姓名的字母顺序排序
*/
TreeSet<Student> ts = new TreeSet<Student>(new Comparator<Student>() {
@Override
public int compare(Student s1, Student s2) {
int num = s1.getAge() - s2.getAge();
int num2 = num == 0 ? s1.getName().compareTo(s2.getName()) : num;
return num2;
}
});
Student s1 = new Student("linqingxia",18,"女");
Student s2 = new Student("wangzhaojun",19,"女");
Student s3 = new Student("yangyuhuan",22,"女");
Student s4 = new Student("diaochan",22,"女");
//添加学生到集合
ts.add(s1);
ts.add(s2);
ts.add(s3);
ts.add(s4);
//遍历
for(Student s : ts)
System.out.println(s.getName() + "," + s.getAge() + "," + s.getSex());
}
}
- 案例1
//成绩排序
//TreeSet集合中存储多个学生信息(姓名,语文成绩,数学成绩),并遍历集合,要求按照总分从高到低排序
import java.util.Comparator;
import java.util.TreeSet;
public class TreeSetDemo {
public static void main(String[] args) {
TreeSet<Student> ts = new TreeSet<>(new Comparator<Student>() {
@Override
public int compare(Student s1, Student s2) {
int num = (s2.getChineseScore() + s2.getMathScore()) -
(s1.getChineseScore() + s1.getMathScore());
int num2 = num == 0 ? s1.getName().compareTo(s2.getName()) : num;
return num2;
}
});
Student s1 = new Student("xiaoming",98,88);
Student s2 = new Student("lisi",97,88);
Student s3 = new Student("wanghua",98,88);
Student s4 = new Student("zhaoliu",87,99);
ts.add(s1);
ts.add(s2);
ts.add(s3);
ts.add(s4);
for(Student s : ts)
System.out.println(s.getName() + "," + s.getChineseScore() + "," + s.getMathScore());
}
}
- 案例二
import java.util.*;
public class RandomNumber {
public static void main(String[] args) {
//编写一个程序,获取10个1-20之间的随机数,要求随机数不重复,并在控制台输出
/*
思路:1、创建一个Set集合
2、创建随机数对象
3、判断集合是否小于10
4、遍历集合
*/
Set<Integer> set = new TreeSet<>(new Comparator<Integer>() {
@Override
public int compare(Integer i1, Integer i2) {
return i1 - i2;
}
});
Random r = new Random();
while(set.size() < 10){
int number = r.nextInt(20) + 1;
set.add(number);
}
for(Integer i : set)
System.out.println(i);
}
}
泛型
- 定义格式
- <类型>:指定一种类型的格式
- <类型1,类型2,…>:指定多种类型的格式
- 将来调用时候给定的类型可以看成是实参,并且实参的类型只能是引用数据类型
- 可变参数
- 格式:修饰符 返回类型 方法名(数据类型… 变量名)
- 范例:public static int num(int… a){}
- 参数数量可变
- 底层实现为数组
Map集合
-
包:java.util.Map
-
Interface Map<k,v>是一个接口,将键映射到对象,不能包含重复的键
-
创建Map对象方式:多态HashMap
-
常用方法
方法名 描述 V put (k key, v value) 添加元素 V remove(Object key) 根据键删除元素 void clear() 移除所有键值对元素 boolean containsKey(Object key) 判断集合是否包含指定键 boolean containsValue(Object value) 判断集合是否包含指定值 boolean isEmpty() 判断结合是否为空 int size() 结合的长度,也就是集合中键值对的个数 -
Map集合的获取功能
方法名 描述 V get(Object key) 根据键获取值 Set< K> keySet() 获取所有键的集合 Collection< v > values() 获取所有值的集合 Set<Map.Entry<K,V>> entrySet() 获取所有键值对对象的集合 -
遍历
- 方式一
- 获取所有键的集合。用keySet()方法实现
- 遍历键的集合,获取每一个键。
- 根据键去找值。用get(Object key)方法实现
- 方式二
- 获取所有键值对对象的集合。使用Set<Map.Entry<K,V>> entrySet()
- 遍历键值对对象的集合,得到每一个键值对对象。使用增强for,得到Map.Entry
- 根据键值对对象获取键和值。用getKey()和getValue()
- 方式一
-
集合嵌套案例
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class Array_MapDemo {
public static void main(String[] args) {
//ArrayList存储Map对象并遍历
/*
思路:1、创建ArrayList集合
2、创建HashMap集合,并添加键值对
3、将HashMap对象添加到ArrayList集合
4、遍历
*/
ArrayList<HashMap<String,String>> array = new ArrayList<HashMap<String,String>>();
HashMap<String,String> hm1 = new HashMap<>();
hm1.put("孙策","大乔");
hm1.put("周瑜","小乔");
HashMap<String,String> hm2 = new HashMap<>();
hm1.put("郭靖","黄蓉");
hm1.put("杨过","小龙女");
HashMap<String,String> hm3 = new HashMap<>();
hm1.put("令狐冲","任盈盈");
hm1.put("林平之","岳灵珊");
//将Map集合添加到List集合
array.add(hm1);
array.add(hm2);
array.add(hm3);
//遍历
for(HashMap<String,String> hm : array){
Set<Map.Entry<String, String>> entrySet = hm.entrySet();
for(Map.Entry me: entrySet){
String key = (String)me.getKey();
String value = (String)me.getValue();
System.out.println(key+","+value);
}
}
}
}
- 用HashMap统计字符串中每个字符出现的格式
import java.util.HashMap;
import java.util.Scanner;
import java.util.Set;
public class CharCount {
//输入一个字符串,统计每个字符出现的次数
//例如:"aaabbcd" 输出a(3)b(2)c(1)d(1)
/*
思路:
1、创建HashMap集合
2、遍历每一个字符
3、将拿到的字符作为键存入Map集合中
4、如果键对应值为null,说明第一次出现,置1;
否则,value++
5、遍历Map集合输出
*/
public static void main(String[] args) {
System.out.println("请输入一个字符串:");
Scanner sc = new Scanner(System.in);
String str = sc.nextLine();
//如果需要对输出结果进行排序,直接将HashMap改为TreeMap即可
HashMap<Character,Integer> hm = new HashMap<Character, Integer>();
for(int i = 0 ; i < str.length(); i++){
char key = str.charAt(i);
Integer value = hm.get(key);
if(value == null)
hm.put(key,1);
else
hm.put(key,++value);
}
//定义输出结果字符串
StringBuilder sb = new StringBuilder();
Set<Character> keySet = hm.keySet();
for(Character key : keySet){
Integer value = hm.get(key);
sb.append(key).append("(").append(value).append(")");
}
System.out.println(sb);
}
}
Collections
-
包:java.util.Collections
-
Collections中的方法都是静态方法,可以直接通过类名.方法名调用
-
针对集合操作的工具类
-
常用方法
方法名 描述 public static <T extends Comparable<? super T>> void sort(List< T > list) 将指定的列表按升序排序 public static void reverse(List<?> list) 反转列表中元素的顺序 public static void shuffle(List<?> list) 使用默认的随机源随机排列指定的列表 -
示例:
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class CollectionsDemo {
public static void main(String[] args) {
List<Integer> list = new ArrayList<Integer>();
list.add(50);
list.add(20);
list.add(10);
list.add(26);
list.add(15);
//升序排序
Collections.sort(list);
System.out.println(list);
//反转
Collections.reverse(list);
System.out.println(list);
//随机打乱-------洗牌
Collections.shuffle(list);
System.out.println(list);
}
}
- 集合应用-----斗地主
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class 斗地主 {
public static void main(String[] args) {
//用集合操作模拟斗地主
/*
思路:
1、定义花色、点数、大小王
2、将花色和点数拼接,以及大小王存储到牌盒(集合)中
3、用shuffle()方法洗牌
4、发牌,轮流将牌发给三位玩家(三个集合存储)
5、输出
*/
List<String> cards = new ArrayList<String>();
//定义花色集合
String[] colors = {"♠","♥","♣","♦"};
//定义点数集合
String[] numbers = {"1","2","3","4","5","6","7",
"8","9","10","J","Q","K","A"};
//拼接
for(String color : colors){
for(String number: numbers){
cards.add(color+number);
}
}
cards.add("小王");
cards.add("大王");
//洗牌
Collections.shuffle(cards);
//发牌
List<String> p1 = new ArrayList<String>();
List<String> p2 = new ArrayList<String>();
List<String> p3 = new ArrayList<String>();
List<String> dipai = new ArrayList<String>();
for(int i = 0; i < 52; i++){
if(i % 3 == 0)
p1.add(cards.get(i));
if(i % 3 == 1)
p2.add(cards.get(i));
if(i % 3 == 2)
p3.add(cards.get(i));
}
dipai.add(cards.get(51));
dipai.add(cards.get(52));
dipai.add(cards.get(53));
System.out.println("玩家1:"+p1);
System.out.println("玩家2:"+p2);
System.out.println("玩家3:"+p3);
System.out.println("玩家4:"+dipai);
}
}
//斗地主升级版
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.TreeSet;
//洗牌、发牌、看牌并且对牌进行排序
/*
思路:
1、创建HashMap,键是编号,值是牌
2、创建ArrayList,存储编号
3、创建花色数组和点数数组
4、从0开始往HashMap里面存储编号,并存储相应对应的牌。
同时往ArrayList里面存储编号
5、洗牌(洗的是编号),用Collections.shuffle()方法
6、发牌(发的也是编号,为了保证编号是排序的,创建TreeSet集合接收
7、定义方法看牌(遍历TreeSet集合,获取编号,到HashMap集合中找对应的牌
8、调用看牌方法
*/
public class 斗地主升级版 {
public static void main(String[] args) {
//创建HashMap,键是编号,值是牌
HashMap<Integer,String> hm = new HashMap<>();
//创建ArrayList,存储编号
ArrayList<Integer> array = new ArrayList<>();
//创建花色数组和点数数组
String[] colors = {"♠","♥","♣","♦"};
String[] numbers = {"3","4","5","6","7",
"8","9","10","J","Q","K","A","2"};
//从0开始往HashMap里面存储编号,并存储相应对应的牌。
int index = 0;
for(String number : numbers){
for(String color : colors){
hm.put(index,color+number);
array.add(index);
index++;
}
}
hm.put(index,"小王");
array.add(index);
index++;
hm.put(index,"大王");
array.add(index);
//洗牌(洗的是编号)
Collections.shuffle(array);
//发牌(发的也是编号,为了保证编号是排序的,创建TreeSet集合接收
TreeSet<Integer> p1 = new TreeSet<Integer>();
TreeSet<Integer> p2 = new TreeSet<Integer>();
TreeSet<Integer> p3 = new TreeSet<Integer>();
TreeSet<Integer> dipai = new TreeSet<Integer>();
for(int i = 0; i < array.size(); i++){
int ind = array.get(i);
if(i >= array.size()-3){ //底牌
dipai.add(array.get(ind));
}else if(i % 3 == 0){
p1.add(ind);
}else if(i % 3 == 1) {
p2.add(ind);
}else {
p3.add(ind);
}
}
//定义方法看牌(遍历TreeSet集合,获取编号,到HashMap集合中找对应的牌
lookPoker("玩家1",p1,hm);
lookPoker("玩家2",p2,hm);
lookPoker("玩家3",p3,hm);
lookPoker("底牌",dipai,hm);
}
public static void lookPoker(String name,TreeSet<Integer> ts, HashMap<Integer,String> hm){
System.out.print(name + "的牌是:");
for(Integer key : ts){
String poker = hm.get(key);
System.out.print(poker + ",");
}
System.out.println();
}
}
十一、I/O
File类
-
是对文件和目录路径名的抽象表示
-
文件和目录可以通过File封装成对象
-
对于File而言,其封装的并不是一个文件,仅仅是一个路径名而已。它可以存在,也可以不存在。使用时需要通过具体的操作将路径的内容转换为具体存在的。
-
构造方法
方法名 描述 File(String pathname) 通过将给定的路径字符串转换为抽象路径名来创建新的File实例 File(String parent, String child) 从父路径名字符串和子路径名字符串创建新的File实例 File(File parent, String child) 从父抽象路径名和子路径名字符串创建新的File实例 -
File类创建功能
方法名 描述 public boolean createNewFile() 当具有该名称的文件不存在时,创建一个由该抽象路径名命名的新文件 public boolean mkdir() 创建单级目录 public boolean mkdirs() 创建多级目录 -
File类的判断和获取、删除功能
方法名 描述 public boolean isDirectory() 测试此抽象路径名表示的文件是否为目录 public boolean isFile() 测试此抽象路径名表示的文件是否为普通文件 public boolean exists() 测试此抽象路径名表示的文件或目录是否存在 public String getAbsolutePath() 返回此抽象路径名的绝对路径名字符串 public String getPath() 将此抽象路径名转换为路径名字符串 public String getName() 返回由此抽象路径名表示的文件或目录的名称 public String[] list() 返回一个字符串数组,命名由此抽象路径名表示的目录中的文件和目录 public File[] listFiles() 返回一个抽象路径名数组,表示由该抽象路径名表示的目录中的文件 public boolean delete() 删除文件或目录,如果删除的是目录,只有目录中没有文件才能删除
字节流
- 如果文件通过windows记事本打开,能都直接读懂则为字符流,否则为字节流;如果不知道应该使用哪种,就使用字节流(万能的流)。
- 包:java.io.InputStream
- InputStream是一个抽象类,表示输入字节流的所有类的超类
- OutputStream表示输出字节流的所有类的超类
对象序列化流
- 对象序列化:就是将对象保存到磁盘中,或者在网络中传输对象
- 这种机制就是使用一个字节序列表示一个对象,该字节序列包含:对象的类型、对象的数据和对象中存储的属性等信息。字节序列写到文件后,相当于文件中持久保存了一个对象信息
- 对象序列化流:ObjectOutputStream
- 对象反序列化流:ObjectInputStream
Properties
- 概述
- 是一个Map体系的集合类
- Properties可以保存到流中或从流中加载
- 通常作为配置文件存在
- 特有方法
方法名 | 描述 |
---|---|
public Object setProperty(String key, String value) | 设置集合的键和值,底层采用HashTable方法put |
public String getProperty(String key) | 使用此属性列表中指定的键搜索属性 |
public Set< String > stringPropertyNames() | 从该属性列表中返回一个不可修改的键集,其中键及其对应的值是字符串 |
十二、线程
-
进程:正在运行的程序。是系统进行资源分配和调用的独立单位,每一个进程都有自己的内存空间和系统资源
-
线程:是进程中的单个顺序控制流,是一条执行路径。可以分为单线程和多线程。
-
多线程的实现方式
-
方式一:继承Thread类
- 定义一个类MyThread继承Thread类
- 在类中重写run()方法
- 创建MyThread类对象
- 启动线程
public class MyThread extends Thread{ @Override public void run() { for(int i = 0; i < 100; i++) System.out.println(i); } } public class MyThreadDemo { public static void main(String[] args) { MyThread my1 = new MyThread(); MyThread my2 = new MyThread(); // my1.run(); run()方法并没有启动线程 // my2.run(); my1.start(); //strat()开启线程 my2.start(); } }
-
方式二:实现Runnable接口
- 定义一个类MyRunnable实现Runnable接口
- 在MyRunnable类中重写run()方法
- 创建MyRunnable类对象
- 创建Thread类的对象,把MyRunnable对象作为构造方法的参数
- 启动线程
/* 采用Runnable接口的好处 1、避免了java单继承的局限性 2、适合多个相同程序的代码去处理同一个资源的情况,把线程和程序的代码、数据分离,较好体现 了面向对象的设计思想 */ public class MyRunnable implements Runnable { @Override public void run() { for (int i = 0; i < 100; i++) System.out.println(Thread.currentThread().getName() + ":" + i); } } public class MyThreadDemo { public static void main(String[] args) { MyRunnable my = new MyRunnable(); Thread th1 = new Thread(my,"线程1"); Thread th2 = new Thread(my,"线程2"); th1.start(); th2.start(); } }
-
-
设置和获取线程名称
- void setName(String name):将线程的名称更改为name
- String getName():返回此线程的名称
-
线程调度
- 分时调度:所有线程轮流使用CPU时间片
- 抢占式调度:让优先级更高的线程优先使用CPU(JAVA采用该方式),优先级高并不代表优先运行,只是代表获取CPU的几率更高。
- public final int getPriority():返回此线程的优先级
- public final void setPriority(int newPriority):更改此线程的优先
-
线程控制
方法名 描述 static void sleep(long millis) 使当前正在执行的线程停留指定毫秒数 void join() 等待这个线程死亡 void setDaemon(boolean on) 将此线程标记为守护线程,当运行的线程都是守护线程时,java虚拟机退出 -
生命周期
-
线程安全的类
- StringBuffer
- 线程安全,可变的字符序列
- 通常使用StringBuilder类,因为支持所有相同的操作,但它更快,因为它不执行同步
- Vector
- 如果不需要线程安全的实现,建议使用ArrayList代替
- HashTable
- 该类实现了一个哈希表,将键映射到值。任何非null对象都可以用作键和值
- 如果不需要线程安全的实现,建议使用HashMap代替
- StringBuffer
-
生产者消费者案例
//包含的类
/*
奶箱类(Box):定义一个成员变量,表示第x瓶奶,提供存储牛奶和获取牛奶的操作
生产者类(Producer):实现Runnbale接口,重写run()方法,调用存储牛奶的操作
消费者类(Customer):实现Runnable接口,重写run()方法,调用获取牛奶的操作
测试类(BoxDemo):里面有main方法,main方法中的步骤如下:
1、创建奶箱对象,这是共享数据区域
2、创建生产者对象,把奶箱对象作为构造方法参数传递,因为在这个类中调用存储牛奶的操作
3、创建消费者对象,把奶箱对象作为构造方法参数传递,因为在这个类中调用获取牛奶的操作
4、创建2个线程对象,分别把生产者对象和消费者对象作为构造方法参数传递
5、启动线程
*/
//奶箱类
public class Box {
private int milk;
//定义一个成员变量,表示奶箱的状态
private boolean state = false;
public synchronized void put(int milk){
//如果有牛奶,等待消费
if(state){
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//如果没有牛奶,生产牛奶
this.milk = milk;
System.out.println("送奶工将第" + this.milk + "瓶奶放入奶箱");
//生产完毕之后,修改奶箱状态
state = true;
//需要唤醒其它等待线程,否则就会一直等待下去
notifyAll();
}
public synchronized void get(){ //需要在同步方法上加上synchronized,否则使用wait()方法会出现异常
//如果没有牛奶,等待生产
if(!state){
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//如果有牛奶,就消费牛奶
System.out.println("用户拿到第" + this.milk + "瓶奶");
//消费完毕后,修改奶箱状态
state = false;
notifyAll();
}
}
//生产者类
public class Producer implements Runnable {
private Box b;
public Producer(Box b) {
this.b = b;
}
@Override
public void run() {
for(int i = 1; i <=99; i++){
b.put(i);
}
}
}
//消费者类
public class Customer implements Runnable {
private Box b;
public Customer(Box b) {
this.b = b;
}
@Override
public void run() {
while(true){
b.get();
}
}
}
//测试类
public class BoxDemo {
public static void main(String[] args) {
//创建奶箱对象
Box b = new Box();
//创建生产者对象,将奶箱对象作为构造方法参数传递
Producer p = new Producer(b);
//创建消费者对象,将奶箱对象作为构造方法参数传递
Customer c = new Customer(b);
//创建2个线程对象,分别把生产者对象和消费者对象作为构造方法参数传递
Thread t1 = new Thread(p);
Thread t2 = new Thread(c);
//启动线程
t1.start();
t2.start();
}
}
十三、网络编程
网络编程入门
-
概述
- 在网络通信协议下,实现网络互联的不同计算机上运行的程序间可以进行数据交换
-
网络编程三要素
- IP地址:标识网络中的设备
- 端口号:唯一标识设备中的应用程序
- 协议:通信双方必须遵循的规则(常见的有TCP、UDP)
-
InetAddress的使用
-
此类表示Internet协议地址
-
常用方法
方法名 描述 static InterAddress getByName(String host) 确定主机名称的IP地址。主机名称可以是机器名称,也可以是IP地址 String getHostName() 获取IP地址的主机名 String geHostAddress() 返回文本显示中的IP地址字符串 -
示例:
import java.net.InetAddress; import java.net.UnknownHostException; public class InterAddressDemo { public static void main(String[] args) throws UnknownHostException { // InetAddress address = InetAddress.getByName("DESKTOP-VU785LO"); // 可以使主机名,也可以是ip地址 InetAddress address = InetAddress.getByName("192.168.137.1"); //获取主机名 String hostName = address.getHostName(); //获取主机ip String ip = address.getHostAddress(); System.out.println(hostName + "的ip地址是:" + ip); } }
-
-
端口号
- 取值0~65535,其中0 ~ 1023之间端口号用于一些知名网络服务和应用,普通应用程序使用1024以上端口。如果端口号被占用,会导致当前程序启动失败。
-
协议
- UDP:用户数据报协议。无连接通信协议,在数据传输时,数据的发送端和接收端不建立逻辑连接。使用UDP协议消耗资源小,通信效率高,所以通常都会用于音频、视频和普通数据的传输。
- TCP:传输控制协议。面向连接的通信协议,即在传输数据之前,在发送端和接收端建立逻辑连接,然后再传输数据,它提供了两台计算机之间的可靠的无差错的数据传输。TCP建立需要进行”三次握手“,断开连接需要”四次挥手“。TCP可以保证数据传输的安全,例如上传文件、下载文件、浏览网页等。
UDP通信程序
-
不可靠的网络协议,它在通信两端各建立一个Socket对象,但是这两个Socket只是发送、接收数据的对象。UDP协议通信双方,无所谓客户端和服务器的概念。
-
java提供了DatagramSocket类作为基于UDP协议的Socket
-
UDP发送数据
- 创建发送端的Socket对象(DatagramSocket)
- 创建数据,并将数据打包
- 调用DatagramSocket对象发送数据
- 关闭发送端
- 示例:
import java.io.IOException; import java.net.*; public class UDPSend { public static void main(String[] args) throws IOException { //创建发送端Socket对象 DatagramSocket ds = new DatagramSocket(); //创建数据并打包 byte[] bys = "hello world,java".getBytes(); int length = bys.length; InetAddress ip = InetAddress.getByName("192.168.137.1"); int port = 10086; DatagramPacket dp = new DatagramPacket(bys,length,ip,port); //创建DatagramSocket对象,发送数据 ds.send(dp); //关闭发送端 ds.close(); } }
-
UDP接收数据
- 创建接收端Socket对象
- 创建一个数据包用于接受数据
- 调用DatagramSocket对象接收数据
- 解析数据包,并将数据在控制台显示
- 关闭接收端
- 示例:
import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.SocketException; public class UDPReceive { public static void main(String[] args) throws IOException { //创建socket对象 DatagramSocket ds = new DatagramSocket(10086); //创建数据包接收数据 byte[] bys = new byte[1024]; DatagramPacket dp = new DatagramPacket(bys,bys.length); //接收数据 ds.receive(dp); //解析数据并显示 byte[] data = dp.getData(); int length = dp.getLength(); String dataString = new String(data,0,length); System.out.println(dataString); //关闭接收端 ds.close(); } }
TCP通信程序
- TCP通信协议是一种可靠的网络协议,它在通信两端各建立一个Socket对象,从而在通信的两端形成网络虚拟链路,一旦建立虚拟的网络链路,两端的程序就可以通过虚拟链路通信
- java对基于TCP协议的网络提供了良好的封装,使用Socket对象来代表两端的通信端口,并通过Socket产生IO流来进行网络通信
- java为客户端提供了Socket类,为服务器端提供了ServerSocket类
- TCP发送数据
- 创建客户端Socket对象
- 获取输出流,写数据
- 释放资源
- TCP接收数据
- 创建服务器端Socket对象(ServerSocket)
- 监听客户端连接,返回一个Socket对象
- 获取输入流,读数据,并将数据显示到控制台
- 释放资源
十四、反射
-
概述
- java反射机制:是指在运行时去获取一个类的变量和方法信息。然后通过获取到的信息来创建对象,调用方法的一种机制。由于这种动态性,可以极大增强程序的灵活性,程序不用在编译期就完成确定,在运行期仍然可以扩展
-
获取Class对象
- 使用类的class属性来获取该类对应的Class对象。例:Student.class将会返回Student类对应的Class对象
- 调用对象的getClass()方法,返回该对象所属类对应的Class对象。
- 使用Class类中的静态方法forName(String className),该方法需要传入字符串参数,该字符串参数的值是某个类的全路径,也就是完整包名的路径
-
类加载器
- 当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过类的加载,类的连接,类的初始化这三个步骤来对类进行初始化。如果不出现意外情况,JVM将会连续完成这三个步骤,所以把这三个步骤统称为类加载或者类初始化。
- 负责将.class文件加载到内存中,并为之生成对应的java.lang.Class对象
- JVM类加载机制
- 全盘负责:就是当一个类加载器负责加载某个Class时,该Class所依赖的和引用的其他Class也将由该类加载器负责载入,除非显示使用另外一个类加载器来载入
- 父类委托:就是当一个类加载器负责加载某个Class时,先让父类加载器视图加载该Class,只有在父类加载器无法加载该类时才尝试从自己的类路径中加载该类
- 缓存机制:保证所有加载过的Class都会被缓存,当程序需要使用某个Class对象时,类加载器先从缓存区搜索该Class,只有当缓存区中不存在该Class对象时,系统才会读取该类对应的二进制数据,并将其转换为Class对象,存储到缓存区