JAVA基础学习笔记
面向对象编程的三大特性
封装
-
1. 封装概述:
- 封装是面向对象编程语言对客观世界的模拟,客观世界里的成员变量都是隐藏在对象内部的,外界无法直接操作 2. 封装的原则:
- 将类的某些信息隐藏在类的内部,外界不能直接访问,只能通过该类提供的方法来实现对隐藏信息的操作和访问
public class A {
private String name;
public void setName(String name){
this.name = name;
}
public String getName() {
return this.name;
}
}
A类中含有一个被private修饰的成员变量name,外界是无法直接访问的,只能通过该类的setname()和getname()方法来设置和获取该name变量。
-
3. 封装的好处:
- 通过方法来控制成员变量的操作,提高了代码的安全性和复用性
继承
继承的关键字为:extends。
public class fu {
public int age = 40;
private int money = 2000;
}
fu类中的money变量是由private关键字修饰的,所以子类无法继承。
public class zi extends fu {
public int age = 20;
public void showAge() {
public age = 30;
System.out.println(age);//输出的是30
System.out.println(this.age);//输出的是20
System.out.println(super.age);//输出的是40
}
super关键字代表父类存储空间的表示,与this关键字用法相似。
多态
看如下的例子:
public abstract class animal {
public int age=20;
public void active() {
System.out.println("animal");
}
public abstract void play();
}
animal类是一个由关键字abstract修饰的抽象类(在一个抽象类中,可以没有抽象方法,但是有抽象方法的类,一定是一个抽象类),
public class dog extends animal{
@Override
public void active() {
System.out.println("dog active");
}
public void play(){
System.out.println("dog play");
}
}
public class cat extends animal{
public int age=40;
public int weight=50;
@Override
public void active(){
System.out.println("cat eat fish");
}
public void play(){
System.out.println("cat play");
}
}
cat类和dog类通过extends关键字继承了animal类。
@Override写在子类中要重写的方法的上方,用于表示该方法是重写的父类中的方法
public class animalPlay {
// public void use(cat c) {
// c.play();
// }
// public void use(dog d) {
// d.play();
// }
public void useAnimal (animal a) {
a.play();
}
}
在animalPaly类中useAnimal()方法中所要传入的参数为animal类型的对象a。在demo测试类中,对象ap在使用use方法的时候,传入的dog类的对象d和cat类的c。这是因为dog类和cat类都是继承animal类的子类。
public class demo {
public static void main(String[] args) {
animal a = new cat();
cat c = new cat();
dog d = new dog();
animalPlay ap = new animalPlay();
ap.use(c);
ap.use(d);
}
}
在demo类中,对象ap在使用use方法的时候,传入了一个cat类的对象c,相当于在调用该方法的时候有这样一条语句
animal c = new cat();
在编译的时候,对于对象c看的是左边animal,认为c是一个animal类的对象,但是在程序运行的时候,c实际上是一个cat类的对象。如果animal类中的paly()方法不是一个抽象方法,而是有方法体的话,在程序运行的时候,也是对象ap的use()方法也是会去执行传入的对象的对应的paly()方法,而不是去执行animal类中的paly()方法。即:编译看左边,运行看右边 。
包
包就类似于一个文件夹,比如有两个同名的类A,但是类A1 和类A2 是在不同的包中的,所以这是两个不同的类,可以类似的理解为不同文件夹里的同名但不同的两个东西。
import 关键字
import java.net.SocketOption;
上述语句表示该类是在 java.net.SocketOption包里的类。
package关键字
package java.net.SocketOption.*;
上述语句用于在该类中加载 java.net.SocketOption这个包内的所有类。
权限修饰符
修饰符 | 同一个类中 | 同一个包中子类无关类 | 不同包的子类 | 不同包的无关类 |
---|---|---|---|---|
private | √ | |||
默认 | √ | √ | ||
protected | √ | √ | √ | |
public | √ | √ | √ | √ |
Scanner的输入
字符的输入
import java.util.Scanner;
public class demo {
public static void main(String[] args) {
Scanner s = new Scanner(System.in);
char c = s.next().charAt(0);
System.out.println(c);
}
}
字符串String和StringBuilder
Stringh和StringBuilder的最大区别就是String不可变,而StringBuiler可变
String
String的构造方法:
char[] ch = {'a','b'.'c'};
String s = new String(ch);//传入一个字符数组
String s = new String();//空字符串对象,不含有任何内容
byte[] bys = {97,98,99};
String s = new Sreing(bys);//传入一个字节数组
String s = "abc";//直接赋值创建一个字符串
String 对象的特点:
1:通过new创建字符串对象时,每一个new都会申请一个新的内存空间,即使内容相同,地址值也不同,也就是即使内容相同的两个String对象,也是两个独立的不同对象
2:以直接赋值的方法创建String对象时
String s1 = "abc";
String s2 = "abc";
在上诉代码中,对于第一行代码,JVM会建立一个String对象放在字符串池中,并给s1参考;第二行则会直接让s2参考字符串中的String对象。所以在本质上,s1和s2是同一个对象。
字符串的比较:
//构造方法得到对象
char[] chs = {'a','b','c'};
String s1 = new String(chs);
String s2 = new String(chs);
//直接赋值得到对象
String s3 = "abc";
String s4 = "abc";
System.out.println(s1==s2);//false,比较的是地址
System.out.println(s1==s3);//false,比较的是地址
System.out.println(s3==s4);//true,比较的是内容
System.out.println(s1.equals(s3);//true,String对象的equals方法需要传入一个字符串,并且比较的是内容
String实现模糊查找
String s = "abc";
s.contains(string);
//当且仅当string是s的子序列时,返回true
StringBuilder
StringBuilder的构造方法,添加和翻转:
StringBuilder sb = new StringBuidler();//也可以使用有参构造方法
sb.append("Hello").append("Wodrld");//StringBuilder中的append方法会在StringBuilder对象中进行添加并返回对象本身
sb.reverse();//将StringBuilder对象中的内容进行翻转
System.out.println(sb.length());//StringBuilder对象的内容长度
StringBuilder和String的相互转化
StringBuilder转化为String
public String toString():通过toString()就可以把StringBuilder转化为String。
String转化为StringBuilder
public StringBuilder(String s):通过构造方法就可以将String转化为StringBuilder。
接口
接口就是一种公共的行为规范,只要符合规范标准,大家都可以使用
JAVA的接口更多体现在对行为的抽象
接口的基本语法
public interface Jumpping {
public abstract void jump();
}
public class Cat implements Jumpping{
@Override
public void jump() {
System.out.println("cat can jump");
}
}
使用关键字implements
public class demo {
public static void main(String[] args) {
// Jumpping j = new Jumpping();//接口无法实例化
Jumpping j = new Cat();
j.jump();//接口多态
}
}
接口的成员特点
public interface Inter {
public int num = 10;
public final int num2 = 20;
// public static final int num3 = 30;
int num3 = 30;//与上一行代码作用相同
//接口没有构造方法
// public Inter() {};
//接口中的方法都是抽象方法,不能有方法体
// public void show() {};
// public abstract void method();
void method();//与上一行代码作用相同
}
//public class InterImpl implements Inter{
public class InterImpl extends Object implements Inter{
public InterImpl() {
super();
}
@Override
public void method() {
}
}
public class InterfaceDemo {
public static void main(String[] args) {
Inter i = new InterImpl();
// i.num = 20;
// 接口中的成员变量默认就是被final修饰的
System.out.println(i.num);
// i.num2 = 40;
// num2被final修饰,不可以重新赋值
System.out.println(i.num2);
// 接口中的成员变量都是被static静态修饰的
System.out.println(Inter.num);
}
}
成员变量:
- 只能是常量
- 默认修饰符:public static final
构造方法:
- 接口没有构造方法,因为接口主要是对行为进行抽象的,是没有具体存在的
- 一个类如果没有父类,默认继承自Object类
成员方法
- 只能是抽象方法
- 默认修饰符:public abstract
类和接口的关系
-
类和类的关系
- 继承关系,只能单继承,但是可以多层继承 类和接口的关系
- 实现关系,可以实现,也可以多实现,还可以在继承一个类的同时实现多个接口 接口和接口的关系
- 继承关系,可以单继承,可以多继承
public interface Inter1 {
}
public interface Inter2 {
}
public interface Inter3 extends Inter2,Inter1{
}
public class InterImpl implements Inter1,Inter2,Inter3 {
}
抽象类和接口的区别
- 成员区别
抽象类 | 变量,常量;有构造方法;有抽象方法,也有非抽象方法 |
---|---|
接口 | 常量;抽象方法 |
- 关系区别
类与类 | 继承,单继承 |
---|---|
类与接口 | 实现,可以单继承,也可以多实现 |
接口与接口 | 继承,单继承,多继承 |
- 设计理念区别
抽象类 | 对类行为,包括属性,行为 |
---|---|
接口 | 对行为抽象,主要是行为 |
在这里强调:!!抽象类是对事物的抽象,接口是对行为的抽象!!
内部类
内部类就是在一个类中再创建一个类
成员内部类
public class Outer {
private int num = 10;
//内部类可以访问外部类的所有东西
public class Inner {
public void show() {
System.out.println(num);
}
}
}
Inner是内部类,Outer是外部类
public class InnerDemo {
public static void main(String[] args) {
Outer.Inner oi = new Outer().new Inner();
oi.show();
}
}
但是,内部类的创建就是为了不让外界看见的。所以内部类一般都是由private修饰的。如下:
public class Outer {
private int num = 10;
private class Inner {
public void show() {
System.out.println(num);
}
}
public void method() {
Inner i = new Inner();
i.show();
}
}
内部类Inner被private修饰了,所以在InnnerDemo测试类中,无法直接创建Inner类的对象。这时,我们就可以使用 method() 方法来间接的访问 show() 方法。
public class InnerDemo {
public static void main(String[] args) {
Outer o = new Outer();
o.method();
}
}
局部内部类
即在一个类的方法中创建一个内部类
public class Outer {
private int num = 10;
public void method() {
int num2 = 20;
//此处不需要权限修饰符修饰
class Inner {
public void show() {
System.out.println(num);
System.out.println(num2);
}
}
Inner i = new Inner();
i.show();
}
}
public class InnerDemo {
public static void main(String[] args) {
Outer o = new Outer();
o.method();
}
}
局部内部类可以访问外部类的成员,也可以访问方法内的局部变量。
匿名内部类
前提:存在一个类或者接口(可以是具体类也可以是抽象类)
本质:是一个继承了该类或者实现了该接口的匿名 对象
public interface Inter {
void show();
}
public class Outer {
public void method() {
//new 类名或者是方法名() {};
Inter i = new Inter() {
@Override
public void show() {
System.out.println("匿名内部类");
}
};
i.show();
i.show();
}
}
public class OuterDemo {
public static void main(String[] args) {
Outer o = new Outer();
o.method();
}
}
前面提到了匿名内部类是一个对象,对象,对象,所以还可以有下面这种用法:
public interface Jumpping {
void jump();
}
public class JumppingOperator {
public void method(Jumpping j) {
j.jump();
}
}
public class JumpDemo {
public static void main(String[] args) {
JumppingOperator jo = new JumppingOperator();
//下面jo对象在使用method方法的时候()里传入的就是一个匿名内部类,本质就是一个没有名字的对象
jo.method(new Jumpping() {
@Override
public void jump() {
System.out.println("cat jump");
}
});
jo.method(new Jumpping() {
@Override
public void jump() {
System.out.println("dog jump");
}
});
}
}
常用类API
MATH
MATH类没有构造方法,其所有方法都是由static修饰的,可以直接通过类名来访问。
方法名 | 说明 |
---|---|
public static int abs(int a) | 返回参数的绝对值 |
public static double ceil(double a) | 返回大于或等于参数的最小double值 |
public static double floor(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的正值[0.0,1.0] |
System
常用方法
方法名 | 说明 |
---|---|
public static void exit(int status) | 终止当前运行的Java虚拟机,非0表示异常终止 |
public static long currentTimeMillis() | 返回当前时间与1970.1.1 的差值(以毫秒为单位) |
Object
Object是类层次结构的根,每个类都可以讲Object作为超类。所有的类都直接或者间接继承自该类。
构造方法:
public Object();
在面向对象中,子类的构造方法都是默认访问父类的无参构造方法。这是因为他们的顶级父类只有一个无参构造方法。
toString() 方法和 equals() 方法
一般都要重写这两个方法,toString() 方法是用来字符串化对象的,equals() 方法是用来比较两个对象内容是否相同的,如果要比较两个对象的地址是否相同,只需要用"=="就可以实现。
Arrays
Arrays类包含用于操作数组的各种方法
方法名 | 说明 |
---|---|
public static String toString(int[] a) | 返回指定数组的字符串表示形式 |
public static void sort(int[] a ) | 按照数字顺序排列指定的数组 |
基本类型包装类
将基本数据类型封装成对象的好处在于可以在对象中定义更多的功能方法操作该数据
比如实现基本数据类型的相互转换
装箱:就是将基本数据类型装换为其包装类
另外,基本数据类型是可以实现自动装箱和拆箱的
Integer i = 100;//自动装箱的实现,底层是Integer i = Integer.ValueOf(100);
i += 100;//自动拆箱的实现,底层是i = i.intValue() +100,这又用到了自动装箱;
异常
异常就是程序出现了不正常的现象
Error:严重问题,不需要处理
Exception:异常类,程序本身可以处理
- RuntimeException:编译期间不检查,出现问题后,需要修改代码
- 非RuntimeException:编译期间必须检查,否则编译无法通过
public void method() throws InPutException{
Scanner s = new Scanner(System.in);
String str = s.next();
if(str == null){
throw InPutException;
}
}
try{
method();
}catch(InPutException ipe){
System.out.println("method方法发生了InPutException错误");
}catch(balabalaException be){
System.out.println("method方法发生了balabalaException错误");
}
集合
集合类体系结构
Collection集合
概述:
- 是单列集合的顶层接口,表示一组对象,这些对象也称为Collection的元素
- JDK不提供此接口的任何直接实现,而是提供更加具体的子接口(Set 和 List)实现
创建Collection集合的对象:
- 采用多态的方式
- 具体的实现类ArrayList
Collection<E> c = new ArrayList<E>();
常用方法:
方法名 | 说明 |
---|---|
boolean add(E,e) | 添加元素 |
boolean remove(Object o) | 从集合中移除指定元素 |
void clear() | 清空集合 |
boolean contains(Object o) | 判断集合中是否存在指定元素 |
boolean isEmpty() | 判断集合是否为空 |
int size() | 集合的长度 |
增强for循环 :
格式
for(元素数据类型 变量名 : 数组或者是Collection集合){
//在此处使用变量名即可,该变量就是元素
}
内部是一个迭代器,所以可能会发生并发修改异常!!
List集合
概述:
- 有序(存储和取出的元素顺序一致)集合(也称为序列),用户可以精确控制列表中每个元素的插入位置。用户可以通过整数索引访问和 搜索元素。
- 与Set集合不同,列表通常允许重复的元素
并发修改异常:
在使用迭代器进行集合的遍历的时候,如果集合的长度发生了改变,就会导致迭代器获取元素中判断预期修改值和实际修改值不一样,就会抛出此异常。也就是说,在使用迭代器对集合进行遍历的时候,不可以对集合进行增添和删减的操作。可以使用for循环来解决此问题。
ArrayList集合
基于数组实现,与LinkedList相比,查询快,添删慢
ArrayList的构造和添加
ArraryList<String> arr = new ArrayList<String>();//<E>E是泛型,创建什么样的集合,就在<>中写什么
//public boolean add(E e) 将指定的元素e插入到集合的末尾
System.out.println(arr.add("abc"));
//这个方法是List集合的特有方法
//oublic void add(int index,E e) 将指定的元素插入到指定的位置,(ArrayList也是从0开始计数的)
arr.add(1,"def");
//输出集合
System.out.println(arr);//输出:[abc,def]
ArrayList的常用方法 (其中有*的表示为List集合的特有方法)
方法名 | 说明 |
---|---|
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() | 返回集合中的元素个数 |
LinkedList集合
基于链表实现,与ArrayList集合相比,查询慢,添删快。
迭代器ListIterator
通过List集合的listIterator()方法得到,是List集合的特有迭代器。其允许程序员沿任一方向遍历列表,在迭代期间,可以对列表进行修改,并返回列表中迭代器的位置。
下面只描述ListIterator的特有方法
方法名 | 说明 |
---|---|
E previous() | 返回列表的上一个元素 |
boolean hasPrevious() | 如果列表有上一元素,则返回true |
void add(E e) | 将指定元素插入列表 |
Set集合
Set集合不包含重复的元素,没有自己的特有方法,其方法全部来自Collection集合接口。此类集合没有带索引的方法,不可以通过普通for循环遍历,只能通过增强的for循环遍历。
Hash值
是JDK根据对象的地址或者字符串或者数字算出来的int类型的数值
Object()类中的hashCode()方法返回该对象的Hash值,对于同一对象,多次调用hashCode()方法返回的其Hash值是相同的,在默认情况下,不同对象的Hash值是不同的(通过方法重写,可以实现不同对象的Hash值是相同的)。
特别的,汉字字符串的Hash值的一样的。 (好像不对,在JDK16中,汉字字符串的Hash值不同)
HashSet集合
此集合对于迭代的顺序不做保证!!!!。(也就是不保证存储和读取的顺序)
该类实现Set接口,由哈希表(实际是HashMap实例)支持
在使用该集合的时候,如果要实现存储的元素不重复,就需要重写存储对象的hascode()方法和equals()方法(alt + insert)
LinkedHashSet集合
此集合由链表和和哈希表支持,链表保证了该集合的存储元素的有序性,哈希表则保证了集合的不重复性。所以,该集合与HashSet集合不同的是,该集合的存储和取出时是有序的。
TreeSet集合
特点:
- 元素有序:此处的有序指的不是存储和取出的顺序的一致性,而是指该集合中的元素会按照一定顺序进行排序,具体排序方式取决于构造方法
TreeSet():根据元素的自然排序进行排序
TreeSet(Comparator comparator):根据指定的比较器进行排序
- 没有带索引的方法,所以不能使用普通的for循环
- 是Set集合,所以没有重复的元素
自然排序Comparable的使用 :
在TreeSet集合中存储对象的时候,如果使用的是Comparable自然排序,那么,该对象的类就要先实现 Comparable 这个接口,并且要重写接口中的 public int compareTo(E e) 方法。在该方法中,如果返回的值为正数,则表示存储的元素比与其比较的那个元素要大,也就是升序排列;负数则相反。如果是0,则代表这两个元素相同,就不会存储该元素。在重写方法的时候,除了要考虑主要条件外,还要考虑排序的次要条件。
比较器Comparator的使用:
比较器的使用是要在创建TreeSet对象的时候,采用有参构造方法,传入一个Comparator接口的,所以我们在创建集合的时候,只需要写一个匿名内部类并重写方法就行。
Map集合
声明:
Interface Map< K,V > K:键的类型 V:值的类型
基本功能:
方法名 | 说明 |
---|---|
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() | 获取所有的键值对的集合 |
Collections
概述:
该类是针对集合操作的工具类。
常用方法:
方法名 | 说明 |
---|---|
publuc static <T extends Comparable<? super T>> void sort(List< T > list) | 将指定列表按升序排列 |
public static void reverse(List<?> list) | 翻转指定列表的顺序 |
public static void shuffle(List<?> list) | 将列表中的元素随机排序 |
迭代器Iterator
迭代器用于实现Collection集合特有的遍历
迭代器通过Collection集合的 Iterator< E > iterator() 方法得到,该方法会返回集合中元素的迭代器。既然迭代器是通过集合的方法得到的,所以,迭代器是依赖于集合存在的。
迭代器的方法
方法名 | 说明 |
---|---|
E next() | 返回迭代的下一个元素 |
boolean hasNext() | 如果迭代具有更多元素,则返回true |
可变参数的使用
在Arrarys工具类中有一个静态方法:
public static < E > List < E > asList(E…e):返回由指定数组支持的固定大小的列表
List接口中有一个静态方法:
public static < E > List < E > of(E…e):返回包含任意数量元素的一个不可变列表
Set接口中有一个静态方法:
public static < E > Set < E > of(E…e):返回包含任意数量元素的一个不可变列表
文件的操作
File类
FIle:是文件和路径名的抽象表示
对于File而言,其封装的并不是一个具体的文件,而是一个路径名。它可以是存在的,也可以是不存在的。在之后的操作中是要将这个路径名通过具体的操作转化为具体存在的
构造方法:
方法名 | 说明 |
---|---|
File(String pathname) | 通过将给定的路径名字字符串转化为抽象路径名来创建新的File实例 |
File(String parent,String child) | 从父路径名字符串和子路径字符串创建新的File实例 |
File(File parent,String child) | 从父抽象路径和子路径名字字符串创建新的File实例 |
File f1 = new File("E:\\java\\java.txt");
File f2 = new File("E:\\java","java.txt");
File f3 = new File(new ("E:\\java"),"java.txt");
System.out.println(f1);
System.out.println(f2);
System.out.println(f3);
//输出都是E:\java\java.txt
File类的创建功能:
方法名 | 说明 |
---|---|
public boolean createNewFile() | 当具有该名称的文件不存在的时候,创建一个由该抽象路径命名的新空文件 |
public boolean mkdir() | 创建由此抽象路径命名的目录 |
public boolean mkdirs() | 创建由此抽象路径命名的目录,包括任何必需但不存在的父目录 |
File类的判断和获取功能
方法名 | 说明 |
---|---|
public boolean isDirectory() | 判断此抽象路径名表示的File是否为目录 |
public boolean isFile() | 判断此抽象路径名表示的File是否为文件 |
public boolean exists() | 判断此抽象路径名表示的File是否存在 |
public String getAbsolutePath() | 返回抽象路径名的绝对路径名字符串 |
public String getPath() | 将此抽象路径名转化为路径名字符串,并返回 |
public String getName() | 返回此抽象路径名表示的文件或者目录的名称 |
public String[] list() | 返回此抽象路径名表示的目录中的文件和目录的名称字符串数组 |
public File[] listFiles() | 返回此抽象路径名表示的目录中的文件和目录中的File数组 |
File类的删除
方法名 | 说明 |
---|---|
public boolean delete() | 删除由此抽象路径名表示的目录或文件 |
文件夹的复制:
import java.io.*;
public class test {
public static void main(String[] args) throws IOException {
File srcFile = new File("123");
File destFile = new File("E:\\");
copyFolder(srcFile,destFile);
}
private static void copyFolder(File srcFile, File destFile) throws IOException{
if(srcFile.isDirectory()){
File newFolder = new File(destFile,srcFile.getName());
if(!newFolder.exists()){
newFolder.mkdir();
}
File[] fileList = srcFile.listFiles();
for(File file : fileList){
copyFolder(file,newFolder);
}
}else {
File newFile = new File(destFile,srcFile.getName());
copy(srcFile,newFile);
}
}
private static void copy(File file, File destFile) throws IOException{
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(destFile));
byte[] bys = new byte[1024];
int len;
while((len = bis.read(bys))!=-1){
bos.write(bys,0,len);
}
bis.close();
bos.close();
}
}
IO流
流:流是一种抽象概念,是对数据传输的总称。也就是说数据在设备间的传输称为流,流的本质是数据传输
IO流就是用来处理设备间的数据传输问题的
如果数据通过Windows自带的记事本打开之后,可以读懂,基于使用字符流,反之则使用字节流。如果不知道该使用哪一个,就使用字节流。
字节流
1.InputStream/OutputStream
这两个类都是抽象类
InputStream:该类继承自Object类,是所有字节输入流的超类。
OutputStream:该类继承自Object类,是所有字节输出流的超类。
写数据/FileOutputStream:
构造方法:
FileOutputStream (String name):创建文件输出流以指定的名称写入文件
FileOutputStream (File file):写入指定的file对象
FileOutputStream (String name,boolean append):如果第二个参数为true,则输出流会将数据写到文件的末尾实现追加写入,而不是写到开头实现重新写入
方法:
方法名 | 说明 |
---|---|
void write(int b) | 将指定的字节写入此文件输入流,一次写入一个字节数据 |
void write(byte[] b) | 将b.length个字节从指定的字节数组写入该字节输入流,一次写入一个字节数组 |
void write(byte[] b,int off,int len) | 将len字节从指定的字节数组开始,从偏移量off开始写入该文件输入流,一次写入一个字节数组的部分数据 |
如何得到字节数组:在String类中有一个getBytes()方法,此方法会返回该字符串对应的字节数组。
FileOutputStream fos = new FileOutputStream("fos.txt");
/*
这里做了三件事
1.创建了fos.txt文件
2.创建了字节输出流对象
3.让字节输出流对象指向创建好的文件
*/
fos.write(97);
//最后都要释放资源 void close():关闭了文件输出流,并释放了与之关联的任何系统资源
fos.close();
注意:如果我们使用记事本打开fos.txt文件的话,看到的是一个a字符而不是97这个数字。假如要写入97的话,就要先写入9,再写入7。
写入一个换行符:在Windows系统的记事本中,换行符是\r\n。
读数据/FileInputStream:
构造方法:
FileInputStream (String name):创建文件输入流以指定的名称读文件
FileInputStream (File file):读指定的file对象
方法:
方法名 | 说明 |
---|---|
int read() | 从该输入流读一个字节的数据(如果文件到达末尾,该方法的返回值为1) |
int read(byte[] b) | 从该输入流读取最多b.length个字节的数据到一个字节数组(返回值是读取到的字节个数。如果文件到达末尾,该方法的返回值为1) |
2.字节缓冲流
该类是为了提高读写文件的速度的,接下来来看构造方法
字节缓冲输出流:BufferOutputStream(OutputStream out)
字节缓冲输入流:BufferInputStream(InputStream in)
字节缓冲流仅仅提供缓冲区,真正实现读写数据还是依靠的基本的字节流对象进行操作,这就是为什么其构造方法中要传入一个字节流对象。
该类的方法与OutputStream和InputStream的使用方法基本一致。
字符流
一个汉字的存储:
- GBK编码:占用2个字节
- UTF-8编码:占用3个字节
String s = "abc";//[97,98,99]
String s = "中国";
byte[] bys = s.getBytes();
byte[] bys = s.getBytes("UTF-8");//[-28,-72,-83,-27,-101,-67]
byte[] bys = s.getBytes("GBK");//[-42,-48,-71,-6]
System.out.println(Arrays.toString(bys))
无论是哪一种编码,汉字的首字节都是负数
由于字节流操作中文并不方便,所以出现了字符流;字符流=字节流+编码表
注意:采用何种的字符集编码,就要采用何种字符集解码,否则就会出现乱码的现象
字符流抽象基类:
- Reader:字符输入流的基类
- Writer:字符输出流的基类
字符流和字节流之间的桥梁:
- InputStreamReader:字节流到字符流的桥梁
- OutputSteamWriter:字符流到字节流的桥梁
上面两个流对象都是可以指定字符集的
构造方法(以InputStreamReader为例):
方法名 | 说明 |
---|---|
InputStreamReader (InputStream in) | 创建一个使用默认字符集(一般为UTF-8)的InputStreamReader |
InputStreamReader (InputStream in,String charsetName) | 创建一个使用这个指定字符集的InputStreamReader |
字符流写数据的方式:
方法名 | 说明 |
---|---|
void write(int c) | 写入一个字符 |
void write(char[] cbuf) | 写入一个字节数组 |
void write(char[] cbuf,int off,int len) | 写入一个字节数组的一部分 |
void write(String str) | 写入一个字符串 |
void write(String str,int off,int len) | 写入一个字符串的一部分 |
字符流读数据的方式:
方法名 | 说明 |
---|---|
int read() | 一次读一个字符数据(文件尾返回-1) |
int read(char[] cbuf) | 一次读一个字符串数据(文件尾返回-1) |
在使用中,一般使用OutputStreamWriter的子类FileWriter和InputStreamReader的子类FileReader作为替代传入字符缓冲流的构造方法。这两个类的构造方法都是可以直接传入文件的String参数的。这两个类在使用构建方法的时候,可以传入第二个参数为true使得文件是追加输入(输出)的。
字符缓冲流的特有方法:
BufferedReader:
- void newLine():写一行行分隔符,行分隔符字符串由系统定义
BufferedWriter:
- public readLine():读一行文字,结果包含行的内容的字符串,不包含任何的行终止符,如果流的结尾已经到达,则为null
finally关键字
被finally关键字控制的语句一定会被执行,除非JVM退出
格式:
try {
//可能会出现异常的代码
}catch {
//异常的处理代码
}finally {
//执行所有的清除操作
}
字符缓冲流:
BufferedWriter和BufferedReader
构造方法(以BufferedWriter为例):
BufferedWriter(Writer out)
BufferedWriter(Writer out,int sz)
使用字符流写数据到文件中
public void output() throws IOException {
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("C:\\Users\\LMER\\Desktop\\studentList.txt"));
for(int i=0;i<=studentList.size()-1;i++){
osw.write(studentList.get(i).toString());
}
osw.close();
}
上代码创建了一个输出流对象osw,通过其 write(String str) 方法来将数据写入。在流对象osw执行close()方法的时候,会先执行流的刷新操作。
小结
File file = new File("E:\\123");//FIle类
InputStream is = new FileInputStream(file);//文件输入字节流
BufferedInputStream bis = new BufferedInputStream(is);//文件输入字节缓冲流
InputStreamReader isr = new InputStreamReader(is);//文件输入字符流
BufferedReader br = new BufferedReader(isr);//文件输入字符缓冲流
BufferedReader br2 =new BufferedReader(new FileReader("E:\\123"));
BufferedInputStream bis2 = new BufferedInputStream(new FileInputStream("E:\\123"));
br和br2是一样的;bis和bis2是一样的
多线程
多线程的实现
1:继承Thread类
- 定义一个类
- 让该类继承Thread类
- 重写该类中的run()方法
- 创建对象
- 启动线程(start()方法)
public class MyThread extends Thread {
@Override
public void run() {
for(int i = 0; i<1000;i++){
System.out.println(i);
}
}
}
public class test {
public static void main(String[] args) {
MyThread mt1 = new MyThread();
MyThread mt2 = new MyThread();
//void start()使线程开始执行;JVM会调用该类的run()方法
mt1.start();
mt2.start();
}
}
2:实现Runnable类
- 定义一个类实现Runnable接口
- 在该类中重写run()方法
- 创建该类的对象
- 创建Thread类的对象,把该类的对象作为构造方法的参数传入
- 启动线程
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 test {
public static void main(String[] args) {
MyRunnable mr = new MyRunnable();
Thread th1 =new Thread(mr,"线程一");
Thread th2 =new Thread(mr,"线程二");
th1.start();
th2.start();
}
}
这种方式实现多线程的好处:
- 避免了Java单继承的局限性
- 适合多个相同程序的代码去处理同一资源的情况,把线程和程序的代码、数据有效的分离开,较好的体现了面向对象的设计思想
线程名字的修改和获取
public class MyThread extends Thread {
public MyThread(){};
public MyThread(String name){
super(name);
}
@Override
public void run() {
for(int i = 0; i<1000;i++){
System.out.println(i);
}
}
}
public class test {
public static void main(String[] args) {
MyThread mt1 = new MyThread("线程一");
MyThread mt2 = new MyThread("线程二");
System.out.println(mt1.getName());//线程一
mt1.setName("改");
System.out.println(mt1.getName());//改
System.out.println(Thread.currentThread().getName());//main
}
}
线程调度
- 分时调度模型:所有线程轮流使用CPU的使用权,平均分配每个线程占用CPU的时间片
- 抢占式调度模型:优先让优先级高的线程使用CPU,如果线程的优先级相同,就随机选择一个,优先级高的线程获取的CPU的时间片要相对多一些
JAVA使用的是抢占式调度模型
方法名 | 说明 |
---|---|
public final int getPriority() | 返回线程的优先级(默认是5) |
public final void setPriority() | 最小值是1,最大值是10 |
线程控制
方法名 | 说明 |
---|---|
static void sleep(long mills) | 使当前正在执行的线程停留(暂停执行)指定的毫秒数 |
void join() | 等待该线程死亡 |
void setDaemon(Boolean on) | 将该线程标记为守护线程。如果运行的线程都是守护线程的话,JVM退出 |
在这里只以第三个方法做个例子
public class test {
public static void main(String[] args) {
MyThread mt1 = new MyThread("关羽");
MyThread mt2 = new MyThread("张飞");
Thread.currentThread().setName("刘备");
mt1.setDaemon(true);
mt2.setDaemon(true);
mt1.start();
mt2.start();
for(int i=0;i < 10;i++){
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
线程“关羽”和线程“张飞”都被设置为了守护线程,而主线程的名字修改为了“刘备”,当主线程执行完毕后,作为守护线程的“关羽”和“张飞”也就没有存在的意义了,他们会在主线程结束后很快结束。