循环
foreach
增强型for循环只能用来取值,却不能用来修改数组里的值:
//User类,下面要用
public class User {
public int id;
public String name;
public User(int id, String name) {
this.id = id;
this.name = name;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
public static void foreachTest() {
int[] ints = {1, 2, 3, 4, 5};
for (int i : ints) {
//无效,值传递,改变这个值并不能改变原有数组的值
i = 1;
System.out.println(i);
}
System.out.println(Arrays.toString(ints));//[1, 2, 3, 4, 5]
}
public static void foreachTest2() {
ArrayList<User> users = new ArrayList<>();
users.add(new User(1, "name1"));
users.add(new User(2, "name2"));
for (User i : users) {
//无效,一开始i存的是users的地址,这样只是改变了i的地址,也和users没关系
i = new User(3, "name3");
}
for (User i : users) {
System.out.println(i);
}
}
public static void foreachTest3() {
ArrayList<User> users = new ArrayList<>();
users.add(new User(1, "name1"));
users.add(new User(2, "name2"));
for (User i : users) {
//有效,通过地址直接改变了users集合内对象的属性值。
i.name = "updateName";
}
for (User i : users) {
System.out.println(i);
}
}
foreach还可以遍历枚举:
public static void foreachTest4() {
//Season枚举类见下段
for (Season season : Season.values()) {
System.out.println(season);
}
}
switch
switch是离散式判断,可以跟枚举类型:
public enum Season {
SPRING,
SUMMER,
AUTUMN,
WINTER
}
public static void enumTest(){
Season season = Season.SPRING;
switch (season){
case SPRING:
System.out.println("spring");
break;
case SUMMER:
System.out.println("summer");
break;
case AUTUMN:
System.out.println("autumn");
break;
case WINTER:
System.out.println("winter");
break;
}
}
继承与接口
多态
分为运算符多态和类多态。
运算符多态:比如"+"运算符可以加数字也可以拼接字符串。
类多态:同一个类的不同对象执行同一方法,结果不同。
类多态条件:
1.两个类继承同一个父类(或同一个接口),然后重写父类的方法(或实现接口方法)。
2.两个类都调用父类(或接口)的方法。
这样可以保证编写代码的时候方法调用的统一,不用根据具体类调一个具体方法。
隐藏
隐藏,就是子类继承父类后,重写了父类的静态方法。
因为静态方法和静态变量不能改变/重写,所以这里的重写实际上是替换、隐藏了父类的静态方法。
//父类
public class Hero {
public String name;
protected float hp;
//类方法,静态方法
//通过类就可以直接调用
public static void battleWin(){
System.out.println("hero battle win");
}
}
//子类
public class ADHero extends Hero{
//隐藏父类的battleWin方法
public static void battleWin(){
System.out.println("ad hero battle win");
}
}
public static void main(String[] args) {
Hero h =new ADHero();
ADHero adHero = new ADHero();
h.battleWin();//hero battle win(执行了父类方法)
adHero.battleWin();//ad hero battle win(执行子类自己的方法)
}
super
实例化一个ADHero(), 其构造方法会被调用,其父类的构造方法也会被调用
父类构造方法先调用,子类构造方法会默认调用父类的无参的构造方法
当父类只有一个有参构造方法,则子类的构造器必须通过super显式调用父类的有参构造方法。
Object类
所有类继承自Object类。
Object类中自带的方法:
- toString():返回当前对象的字符串表达
- finalize():
当一个对象没有任何引用指向的时候,它就满足垃圾回收的条件。
当它被垃圾回收的时候,它的finalize() 方法就会被调用。
finalize() 不是开发人员主动调用的方法,而是由虚拟机JVM调用的。 - equals():判断两个对象的内容是否相同,不是判断地址,判断地址请用“==”
- hashCode():返回一个对象的哈希值
- 线程同步相关方法:wait()、notify()、notifyAll(),讲线程再说
- getClass():返回一个对象的类对象,反射用的多
final
- 修饰的类不能被继承
- 修饰的方法不能被重写
- 修饰的基本类型变量和引用变量只能赋值一次,不可更改
static
static可以修饰成员变量(静态变量)、成员方法(静态方法)、代码块
静态变量/静态方法:所有该类的实例均共享
静态代码块:在类被加载时,执行一次。
接口
接口中声明的属性,只能是public static final修饰
内部类
静态内部类和非静态内部类和主类里面的属性、方法平起平坐。
匿名内部类和本地类则既可以放在主类里也可以放在主类的方法、代码块内部。
非静态内部类
public class xxx{
class yyy{
}
}
外部类存在,才能创建内部类
new xxx().new yyy()
静态内部类
public class xxx{
static class yyy{
}
}
不需要外部类存在,就能创建内部类
new xxx.yyy()
匿名内部类
匿名类指的是在声明一个类的同时实例化它,使代码更加简洁精练
通常情况下,要使用一个接口或者抽象类,都必须创建一个子类
有的时候,为了快速使用,直接实例化一个抽象类,并“当场”实现其抽象方法。
既然实现了抽象方法,那么就是一个新的类,只是这个类,没有命名。这样的类,叫做匿名类
public abstract class Abstract_Object {
public abstract void abstract_method();
}
public class Main {
public static void main(String[] args) {
Abstract_Object object = new Abstract_Object() {
@Override
public void abstract_method() {
System.out.println("abstract_method()");
}
};
object.abstract_method();
}
}
本地类
本地类可以理解为有名字的匿名类
public abstract class Abstract_Object {
public abstract void abstract_method();
}
public class Main {
public static void main(String[] args) {
class NewObject extends Hero {
public void abstract_method() {
System.out.println("abstract_method()");
}
}
NewObject object = new NewObject();
object.abstract_method();
}
}
默认方法
接口中可以写默认方法,实现类就可以不用重新实现了
public interface PersonMethod {
void die();
//接口方法的默认实现
default public void eat(){
System.out.println("eat()");
}
}
public class Main {
public static void main(String[] args) {
//这里用个本地类来测试
class Person implements PersonMethod{
//eat()方法可以不用实现
@Override
public void die() {
System.out.println("die");
}
}
Person person = new Person();
person.eat();
person.die();
}
}
异常处理
基本结构
try{
//todo
}
catch(FileNotFoundException e){
e.printStackTrace();
}
finally{
//不管有无异常,一定执行
}
多异常处理
try {
//todo
} catch (FileNotFoundException | ParseException e) {
if (e instanceof FileNotFoundException)
System.out.println("FileNotFoundException");
if (e instanceof ParseException)
System.out.println("ParseException");
e.printStackTrace();
}
throw与throws
throws表示该方法会把可能的异常向上层抛出,但不能总往上抛,总有一层要try、catch、finally
throw是在方法体中代码执行过程中手动抛出了异常。
private static void method1() {
try {
method2();
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
private static void method2() throws FileNotFoundException {
throw new FileNotFoundException();
}
异常与错误的分类
异常类
异常类的继承图:
IO流
分类
按方向分
到内存程序中,是输入流,从程序到文件啥的,是输出流。
按操作方式分
按操作对象分
选择
知道了 IO 流有这么多分类,那我们在使用的时候应该怎么选择呢?比如什么时候用输出流?什么时候用字节流?可以根据下面三步选择适合自己的流:
- 首先自己要知道是选择输入流还是输出流。这就要根据自己的情况决定,如果想从程序写东西到别的地方,那么就选择输入流,反之就选输出流;
- 然后考虑你传输数据时,是每次传一个字节还是两个字节,每次传输一个字节就选字节流,如果存在中文,那肯定就要选字符流了。
- 通过前面两步就可以选出一个合适的节点流了,比如字节输入流 InputStream,如果要在此基础上增强功能,那么就在处理流中选择一个合适的即可。
常见流的使用
import com.coderhao.User;
import java.io.*;
import java.security.PublicKey;
public class FileTest {
public static void main(String[] args) {
//相对路径
File file = new File("a.exe");
String path = file.getAbsolutePath();
System.out.println(path);//在项目路径下,和src路径平行
//绝对路径
File file1 = new File("f:/a.exe");
String path1 = file1.getAbsolutePath();
System.out.println(path1);//f:\a.exe
//字节流
File file2 = new File("zijieliu.txt");
//把流定义在try()里,try,catch或者finally结束的时候,会自动关闭,否则要手动关闭
//先用哪个流就要把哪个流放前面
try (FileOutputStream fileOutputStream = new FileOutputStream(file2)) {
//输出
String data = new String("hello有中文");
byte[] bytes = data.getBytes("utf-8");
fileOutputStream.write(bytes);
} catch (IOException e) {
e.printStackTrace();
}
try (FileInputStream fileInputStream = new FileInputStream(file2)) {
//输入
byte[] all = new byte[(int) file2.length()];
fileInputStream.read(all);
String s = new String(all, "utf-8");
System.out.println(s);
} catch (IOException e) {
e.printStackTrace();
}
//字符流
File file3 = new File("zifuliu.txt");
try (FileWriter fileWriter = new FileWriter(file3)) {
//输出字符
String data = "字符流写出字符。";
fileWriter.write(data);
} catch (IOException e) {
e.printStackTrace();
}
//FileReader是不能手动设置编码方式的,为了使用其他的编码方式,只能使用InputStreamReader来代替
//并且使用new InputStreamReader(new FileInputStream(f),"utf-8"); 这样的形式
try (InputStreamReader inputStreamReader = new InputStreamReader(new FileInputStream(file3), "utf-8")) {
//输入字符
char[] all = new char[(int) file3.length()];
inputStreamReader.read(all);
String s = new String(all);
System.out.println(s);
} catch (IOException e) {
e.printStackTrace();
}
//缓存流
//以介质是硬盘为例,字节流和字符流的弊端:在每一次读写的时候,都会访问硬盘。 如果读写的频率比较高的时候,其性能表现不佳。
//缓存流在写入数据的时候,会先把数据写入到缓存区,直到缓存区达到一定的量,才把这些数据,一起写入到硬盘中去。按照这种操作模式,就不会像字节流,字符流那样每写一个字节都访问硬盘,从而减少了IO操作
File file4 = new File("huancunliu.txt");
//Socket编程中,尽量用PrintWriter取代BufferedWriter
try (FileWriter fileWriter = new FileWriter(file4); PrintWriter printWriter = new PrintWriter(fileWriter)) {
//输出
printWriter.println("阿联军的啦aaa");
printWriter.println("ashdkjh萨拉丁");
} catch (IOException e) {
e.printStackTrace();
}
try (FileReader fileReader = new FileReader(file4); BufferedReader bufferedReader = new BufferedReader(fileReader)) {
//一行一行的读
while (true) {
String line = bufferedReader.readLine();
if (line == null) {
break;
}
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}
//数据流
//使用数据流的writeUTF()和readUTF()可以进行数据的格式化顺序读写
File file5 = new File("shujuliu.txt");
try (FileOutputStream fileOutputStream = new FileOutputStream(file5); DataOutputStream dataOutputStream = new DataOutputStream(fileOutputStream)) {
//输出
dataOutputStream.writeBoolean(true);
dataOutputStream.writeUTF("安理会是的hello");
dataOutputStream.writeDouble(13.58);
} catch (IOException e) {
e.printStackTrace();
}
try (FileInputStream fileInputStream = new FileInputStream(file5); DataInputStream dataInputStream = new DataInputStream(fileInputStream)) {
//数据的读写要一一对应,有顺序的
boolean b = dataInputStream.readBoolean();
System.out.println(b);
String s = dataInputStream.readUTF();
System.out.println(s);
double v = dataInputStream.readDouble();
System.out.println(v);
} catch (IOException e) {
e.printStackTrace();
}
//对象流,指的是可以直接把一个对象以流的形式传输给其他的介质(序列化过程)
//只能是implements Serializable 的类的对象
File file6 = new File("duixiangliu.txt");
try (FileOutputStream fileOutputStream = new FileOutputStream(file6); ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream)) {
User user1 = new User(001, "name1");
User user2 = new User(002, "name2");
objectOutputStream.writeObject(user1);
objectOutputStream.writeObject(user2);
} catch (IOException e) {
e.printStackTrace();
}
try (FileInputStream fileInputStream = new FileInputStream(file6); ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream)) {
//写两个对象也只能读开头的一个对象
User user = (User) objectInputStream.readObject();
System.out.println(user);
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
//追加
//以上写入都是覆盖,如果要追加,可以1.改变文件指针 2.创建写入对象时第二个参数为true
File file7 = new File("zhuijia.txt");
for (int i = 0; i < 5; i++) {
appendTest(file7);
}
try (FileReader fileReader = new FileReader(file7); BufferedReader bufferedReader = new BufferedReader(fileReader)) {
while (true) {
String s = bufferedReader.readLine();
if (s == null) {
break;
}
System.out.println(s);
}
} catch (IOException e) {
e.printStackTrace();
}
}
public static void appendTest(File file) {
//打开一个写文件器,构造函数中的第二个参数true表示以追加形式写文件
try (FileOutputStream fileOutputStream = new FileOutputStream(file, true); PrintWriter printWriter = new PrintWriter(fileOutputStream)) {
printWriter.print("start中英文结合end");
printWriter.print(12.55);
//强制把缓存中的数据写入硬盘,无论缓存是否已满
printWriter.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
}
集合
集合继承图
数组式的查找快,链表式的增删快,二叉树式的排序快,hash式的查找、增删都快。
HashSet无序,list有序。
常见使用
import com.coderhao.object.User;
import java.util.*;
public class CollectionTest {
public static void main(String[] args) {
ArrayList<User> users = new ArrayList<>();
users.add(new User(001, "name1"));
users.add(new User(002, "name2"));
//迭代器iterator的for写法
for (Iterator<User> iterator = users.iterator(); iterator.hasNext(); ) {
User next = iterator.next();
System.out.println(next);
}
//除了实现了List接口外,LinkedList还实现了双向链表结构Deque,可以很方便的在头尾插入删除数据
final LinkedList<User> linkedList = new LinkedList<>();
//尾部添加
linkedList.add(new User(1, "name1"));
//头部添加
linkedList.addFirst(new User(0, "name-first"));
System.out.println(linkedList);
//查看头部
System.out.println(linkedList.getFirst());
//移除头部
System.out.println(linkedList.removeFirst());
System.out.println(linkedList);
//LinkedList 除了实现了List和Deque外,还实现了Queue接口(队列)。Queue是先进先出队列FIFO
//offer 在最后添加元素,poll 取出第一个元素,peek 查看第一个元素
linkedList.offer(new User(2, "name2"));
System.out.println(linkedList);
//poll取出和remove类似
System.out.println(linkedList.poll());
System.out.println(linkedList);
//peek查看不会移除
System.out.println(linkedList.peek());
System.out.println(linkedList);
//与FIFO(先入先出的)队列类似的一种数据结构是FILO先入后出栈Stack
//push放入栈顶,peek查看栈顶,pop取出栈顶
class StackTest extends Stack {
}
//二叉树
class Node {
// 左子节点
public Node leftNode;
// 右子节点
public Node rightNode;
// 值
public Object value;
// 插入 数据
public void add(Object v) {
// 如果当前节点没有值,就把数据放在当前节点上
if (null == value)
value = v;
// 如果当前节点有值,就进行判断,新增的值与当前值的大小关系
else {
// 新增的值,比当前值小或者相同
if ((Integer) v - ((Integer) value) <= 0) {
if (null == leftNode)
leftNode = new Node();
leftNode.add(v);
}
// 新增的值,比当前值大
else {
if (null == rightNode)
rightNode = new Node();
rightNode.add(v);
}
}
}
// 中序遍历所有的节点
public List<Object> values() {
List<Object> values = new ArrayList<>();
// 左节点的遍历结果
if (null != leftNode)
values.addAll(leftNode.values());
// 当前节点
values.add(value);
// 右节点的遍历结果
if (null != rightNode)
values.addAll(rightNode.values());
return values;
}
}
int[] ints = {2, 31, 24, 65, 23, 43};
Node root = new Node();
for (int i : ints) {
root.add(i);
}
List<Object> values = root.values();
System.out.println(values);//[2, 23, 24, 31, 43, 65]
//HashMap要比List效率高,这是一种用空间换时间的思维方式得到的结果
//Set中元素没顺序,HashSet底层就是HashMap,只是不用HashMap的v,只用k来存数据。
//排序
ArrayList<User> list = new ArrayList<>();
list.add(new User(12, "name12"));
list.add(new User(18, "name18"));
list.add(new User(7, "name7"));
Comparator<User> userComparator = new Comparator<User>() {
@Override
public int compare(User o1, User o2) {
//升序排列
if (o1.id > o2.id) {
return 1;
} else {
return -1;
}
}
};
Collections.sort(list, userComparator);
System.out.println(list);
//聚合操作lambda
//通过聚合操作找出来id第三高的user名称
String s = list
.stream()
.sorted((h1, h2) -> h1.id > h2.id ? -1 : 1)
.skip(2)
.map(h -> h.name)
.findFirst()
.get();
System.out.println(s);
}
}
注:lambda中的聚合操作
管道原:
把Collection切换成管道源很简单,调用stream()就行了。heros.stream()
但是数组却没有stream()方法,需要使用Arrays.stream(hs)
中间操作:
结束操作:
泛型
泛型
//泛型
public class FanxingTest<T> {
LinkedList<T> values = new LinkedList<T>();
public T getT(int index) {
return values.get(index);
}
public void addT(T t) {
values.add(t);
}
public static void main(String[] args) {
FanxingTest<User> users = new FanxingTest<>();
users.addT(new User(1, "name1"));
System.out.println(users.getT(0));
}
}
<? extends T>泛型
表示这是一个T泛型或者其子类泛型
该类的对象,一定是能转成T的
<? super T>泛型
表示这是一个T泛型或者其父类泛型
子父类泛型转换
子类可以转父类,但子类泛型不能转父类泛型。