学习java
记录自己学习java
继承 继承提高了代码的复用性以及维护性 子类可以有父类的内容,也可以有自己特有的内容
this 代表本类对象的引用 super 代表父类存储空间的标识(可以理解为父类对象引用)
this/super.成员方法()/变量 ->本类/父类的成员方法/变量 this/super()构造方法 ->本类/父类的构造方法
继承中构造方法的访问
子类中的构造方法会默认都会访问父类中的无参构造方法
每一个子类构造方法的第一条语句默认是 super()
父类没有无参构造方法
1.通过super关键字去显示的调用父类中的有参数的构造方法
2.在父类中自己提供一个无参构造方法 ***》推荐自己给出无参构造方法
继承中成员方法的访问
子类对象访问方法:子类成员范围找 父类成员范围找
方法重写:子类中出现了和父类中一样的方法声明
子类中重写的方法有自己特定的功能,还能沿袭父类被重写的方法功能
方法重写的注意事项
父类中的私有方法子类中是不能重写的(父类私有子类成员是不能继承的)
子类访问父类的方法 访问权限修饰符要比父类的权限修饰符高 (public > 默认 > 私有)
同一个类中private 默认 protected public
同一个包中 默认 protected public
不同包的子类 protected public
不同包 无关类 public
状态修饰符
final最终态 修饰成员方法 成员变量 类
final修饰的方法不能被重写 final修饰成员变量不能被修改 final修饰的类不能被继承
static静态
final修饰基本类型 值不能变
final修饰引用类型 地址值不能变 内容可以变
static 修饰的特点 被类的所有对象共享 可以通过类名调用(推荐) 也可以通过对象名调用
static静态访问特点
非静态的成员方法可以访问静态和非静态的成员方法和变量
静态的成员方法只能访问静态的成员变量的成员方法
静态成员方法只能访问静态成员
多态
同一个对象,在不同时刻表现出来的不同形态
例如:猫是猫 猫是动物(猫 cat = new 猫() 动物 animal = new 猫())
前提和体现:
有继承/实现关系
有方法重写
有父类引用指向子类对象
体现多态 有父类引用指向子类对象 Animal animal = new Cat();
多态中成员变量的访问特点(原因是成员方法有重写,成员变量没重写)
(animal.eat();编译和运行都看左边 Animal animal 访问成员变量
animal.eat();编译看左边运行看右边 Animal animal = new Cat() 访问成员方法)
体现多态 有父类引用指向子类对象
Animal animal = new Cat()
animal.eat();编译和运行都看左边 Animal animal 访问成员变量 得到的是父类中的成员变量的值
animal.eat();编译看左边运行看右边 Animal animal = new Cat() 访问成员方法 得到的是子类中重写后成员方法的值
多态的好处的弊端
好处:提高了程序的可扩展性
(方法的时候,使用父类型作为参数,将来使用的时候,使用具体的子类型参与操作)
弊端:不能访问子类特有的功能
多态中的转型(实现访问子类特有功能)
向上转型(从子到父 父类引用指向子类对象)
向下转型(从父到子 父类引用转为子类对象)把父类对象强转为子类对象
抽象类
一个没有方法体的方法应该被定义为抽象方法,而雷总如果有抽象方法,该类必须定义为抽象类
抽象类
抽象类的特点
使用abstract关键字修饰,抽象类中不一定有抽象方法,有抽象方法的类一定是抽象类
抽象类里面可以有不是抽象方法的方法,也可以没有抽象方法
抽象类不能直接实例化创建对象,参照多态子类对象实例化创建对象
子类要么继承父类的抽象方法 要么就将子类用abstract修饰成为一个抽象类
抽象类的成员特点
可以有成员变量可以是变量,也可以是常量
可以有成员方法,既可以有非抽象方法,也可以有抽象方法
构造方法不能实例化,但是可以用于子类访问父类数据的初始化
非抽象方法提高代码的复用性(继承)
父类中抽象方法的作用是限定子类必须要做某些事情
接口interface:
公共的规范标准 体现在对行为的抽象
接口特点:
使用interface关键字修饰
类实现接口使用implements关键字
抽象的 不能被实例化
接口实例化 采用多态的形式 用它的实现类对象进行实例化 接口叫多态
多态的形式: 基本不用(具体类多态) 抽象类多态 接口多态
多态的前提:有继承或者实现关系,有方法重写,有父(类/接口)引用指向(子/实现)类对象
接口的实现类: 要么重写接口中的所有抽象方法,要么是抽象类
接口的成员特点
成员变量:只能是常量 默认修饰符public static final(这个变量默认被final修饰 即接口中没有变量)
接口中的成员变量默认是被final、static关键字修饰 不写也可以
接口实现类 接口名+Impl 例如 : InterImpl
访问成员变量可以通过接口名直接访问
成员方法:接口里面没有构造方法
接口里面不能有非抽象方法
只能抽象方法、接口里面的方法默认带了 public abstract修饰
一个类如果没有父类,默认继承自Object类
类和接口的关系
类和类的关系
继承关系:只能单继承,但是可以多层继承
类和接口的关系
实现关系:可以单实现,也可以多实现,还可以在继承一个类的同事实现多个接口
接口和接口的关系
继承关系,可以单继承,也可以多继承
抽象类和接口的区别
语法区别
成员区别
抽象类 变量,常量,有构造方法,有抽象方法,也有非抽象方法
接口 常量 抽象方法
关系区别
类与类 继承,单继承
类与接口 实现,可以单实现,也可以多实现
接口与接口 继承,单继承,duojic
设计理念区别
抽象类 对类抽象,包括属性、行为
接口 对行为抽象,主要是行为
抽象类名和接口名作为形参和返回值
抽象类名作为形参和返回值
方法的形参是抽象类名,其实需要的是该抽象类的子类对象
方法的返回值是抽象类名,其实返回的是该抽象类的子类对象
接口名作为形参和返回值
方法的形参是接口名,其实需要的是该接口的实现类对象
方法的返回值是接口名,其实返回的是该接口的实现类对象
内部类
在类的成员位置:成员内部类
在类的局部位置:局部内部类
访问特点:内部类可以直接访问外部类的成员,包括私有
外部类要访问内部类的成员,必须创建对象
局部内部类
是在方法内定义的类,外界无法直接使用,需要在方法内部创建对象使用
局部内部类可以直接访问外部类的成员,也可以访问方法内的局部变量
匿名内部类也是局部内部类
本质:是一个继承了该类或者实现了该接口的子类匿名对象
存在的前提:存在一个类或者接口,类可以是具体的类也可以是抽象类(抽象类居多)
new 名(){重写方法};
冒泡排序
对要进行排序的数据中的相邻的数据进行两两比较,将较大的数据放在后面,
依次对所有的数据进行操作,直至所有的数据按照要求完成排序
n个数据比较n-1次 每一次比较晚,会少一个数据
Arrays类是工具类:构造方法用private修饰 成员用public static修饰
基本类型包装: 只要是对象,在使用前就必须进行不为null的判断
SimpleDateFormat类 y-年 M-月 d-日 H-时 m-分 s-秒
格式化 转化 从Date到String format(date)
解析 从String到Date parse(date1)
Calendar类
为某一时刻的一组日历字段之间的转换提供了一些方法,并为操作日历字段提供了一些方法
提供了一个类方法getInstance用于获取Calendar对象,其日历字段已使用当前日期和事件初始化
Calendar rightNow = Calendar.getInstance();
异常
出现了不正常的情况
JVM的默认处理方案
把异常名称,异常原因以及异常出现的位置等信息输出在控制台
程序停止执行
异常处理
try - catch -
//从try这里面的代码开始执行,出现异常,会自动生成一个异常类对象,该异常类对象将被提交给java运行时系统
当java运行时系统收到异常对象时,会到catch中去找匹配的异常类,找到后进行异常处理
执行完毕后,程序还可以继续往下执行
try{
可能出现异常的代码;
}catch(异常类名 变量名){
异常的处理代码;
}
throws
使用try catch 处理异常
Throwable类 异常的祖先类
的三个方法:toString() getMessage() printStackTrace()
编译时异常和运行时异常区别
编译时异常和运行时异常,也被称为受检异常和非受检异常
所有的RuntimeException类及其子类被称为运行时异常,其他的异常都是编译时异常
编译时异常:必须显示处理,否则程序就会发生错误,无妨通过编译 受检异常
运行时异常:无需显示处理,也可以和编译时异常一样处理 非受检异常
throws 处理异常
throws 异常类名; 这个格式是跟在方法的括号后面的
throws 只是抛出异常 要想解决 还是得捕获异常 try catch
方法如果解决不了异常,先抛出异常,然后让调用者捕获异常try catch
虽然通过try catch 对异常进行处理,但是并不是所有的情况我们都有权限进行异常的处理
编译时异常必须要进行处理,
try catch 或者 throws 采用后者,将由调用者处理
运行时异常可以不处理,出问题后,需要回来修改代码
自定义异常类
public class 异常类名 extends Exception{
无参构造
有参构造
}
Teacher 使用自定义异常类 ScoreException
TeacherTest
throws和throwde 区别
throws
用在方法的声明后面,跟的是异常类名
表示抛出异常,由该方法的调用者处理
表示出现异常的一种可能性,并不一定会发生这些异常
throw 用在方法体内,跟的是异常对象名
表示抛出异常,由方法体内的语句处理
执行throw一定是跑出了某种异常
集合体系结构
集合进阶 Collection List Set 泛型 Map Collections
Collection(接口) 单列集合 是单列集合的顶层接口表示一组对象,这些对象也称为Collection的元素
单列集合 (李四) List(接口) 可重复 ArrayList(实现类) LinkedList(实现类)
Set(接口) 不可重复 HashSet(实现类) TreeSet(实现类)
Map(接口) 集合 双列集合(01 李四) Map HashMap(实现类)
CollectionDemo Collection使用
常用方法的使用 add remove clear contains isEmpty size
Collection 常用方法:
boolean add(E e)确保此集合包含指定的元素(可选操作)。
boolean isEmpty()如果此集合不包含元素,则返回 true 。
boolean remove(Object o)从该集合中删除指定元素的单个实例(如果存在)(可选操作)。 更正式地,删除元素e ,使得(o==null ? e==null : o.equals(e)) ,如果该集合包含一个或多个这样的元素。 如果此集合包含指定的元素(或等效地,如果此集合由于调用而更改),则返回true 。
int size()返回此集合中的元素数。 如果此收藏包含超过Integer.MAX_VALUE个元素,则返回Integer.MAX_VALUE 。
boolean contains(Object o)如果此集合包含指定的元素,则返回true 。 更正式地,返回true如果且仅当该集合至少包含一个元素e使得(o==null ? e==null : o.equals(e)) 。
void clear()从此集合中删除所有元素(可选操作)。 此方法返回后,集合将为空。
Collection 集合的遍历 terator iterator()
iterator:迭代器,集合的专用遍历方式
Iterator iterator() 返回此集合中的元素的迭代器。
boolean hasNext() 如果迭代具有更多元素,则返回 true 。
E next() 返回迭代中的下一个元素。
Collection集合的使用步骤:
1创建集合对象 Collection<String> collection = new ArrayList<>();
2添加元素 collection.add("hello");
2.1创建元素 String s ="hello";
2.2添加元素到集合 collection.add(s)
2添加元素到集合 collection.add("hello");
3遍历集合
3.1通过集合对象获取迭代器对象 Iterator<String> iterator = collection.iterator();
3.2通过迭代器对象的hasNext()方法判断是否还有元素
3.3通过迭代器的next()方法获取下一个元素
List集合
List集合:有序集合(也称为序列 )。 该界面的用户可以精确控制列表中每个元素的插入位置。 用户可以通过整数索引(列表中的位置)访问元素,并搜索列表中的元素。
与Set集合不同,列表通常允许重复的元素。
List集合特点
有序:存储和取出的元素顺序一致
可重复性:存储的元素可以重复
List集合的特有方法
void add(int index,E element)将指定的元素插入此列表中的指定位置(可选操作)。
将当前位于该位置的元素(如果有)和任何后续元素(向其索引添加一个)移动。
参数 index - 要插入指定元素的索引 element - 要插入的元素
E remove(int index)删除该列表中指定位置的元素(可选操作)。
将任何后续元素移动到左侧(从其索引中减去一个元素)。 返回从列表中删除的元素。
参数 index - 要删除的元素的索引
结果 该元素以前在指定的位置
E set(int index,E element)用指定的元素(可选操作)替换此列表中指定位置的元素。
参数 index - 要替换的元素的索引 element - 要存储在指定位置的元素
E get(int index)返回此列表中指定位置的元素。
参数 index - 要返回的元素的索引
可以通过索引遍历集合
List集合 并发修改异常
产生原因:迭代器遍历的过程中,通过集合对象修改了集合中元素的长度,造成了迭代器获取元素中判断预期修改值和实际修改值不一致
解决方案:用for循环遍历,然后用集合对象做对应的操作即可
列表迭代器ListIterator
ListIterator 列表迭代器 通过List集合的iterator()方法得到,所以说它是List集合特有的迭代器
用于允许程序员沿任一方向遍历列表的列表的迭代器,在迭代期间修改列表,并获取列表中迭代器的当前位置。
常用方法:
E next()返回列表中的下一个元素,并且前进光标位置。
boolean hasNext()返回true如果遍历正向列表,列表迭代器有多个元素。
E previous()返回列表中的上一个元素,并向后移动光标位置
boolean hasPrevious()返回true如果遍历反向列表,列表迭代器有多个元素。
void add(E e)将指定的元素插入列表(可选操作)。
增强for:简化数组和Collection集合的遍历
实现Iterator接口的类允许其对象成为增强for语句的目标
它是jdk5之后出现的,其内部原理是一个Iterator迭代器
for(元素数据类型 变量名:数组或者Collection集合){
在此处使用变量即可,该变量就是元素
}
迭代器遍历 集合特有的遍历方式
for循环遍历 带有索引的遍历方式
增强for 最简便的遍历方式
栈 后进先出
队列 先进先出
数组 查询快 增删慢
链表 查询慢 增删快
分别使用ArrayList 和LinkedList完成存储字符串并遍历
List集合子类的特点
List集合常用子类:
ArrayList 底层数据结构是数组 可调整大小的数组的实现List接口。 数组 查询快 增删慢
LinkedList 底层是链表 双链表实现了List和Deque接口 链表 查询慢 增删快
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类型的数值
Object类中的方法可以获取对象的哈希值
public int hashCode()返回对象的哈希码值。 支持这种方法是为了散列表,如HashMap提供的那样 。
哈希值
同一个对象多次调用hashCode()方法返回的值相同
默认情况下不同对象的哈希值是不同的
可以通过hashCode()方法重写,可以实现不同对象的哈希值是相同的
HashSet集合 无序
底层数据结构是哈希表
HashSet集合要保证元素的唯一性,就需要重写hashCode()和equals()方法
对集合的迭代顺序不作任何保证,也就是说不保证存储和取出元素的顺序一致
没有带索引的方法,所以不能使用普通for循环遍历
由于是Set集合,所以不包括重复元素的集合
此类实现Set接口,由哈希表(实际为HashMap实例)支持。 对集合的迭代次序不作任何保证;
HashSet集合 HashSet集合要保证元素的唯一性,就需要重写hashCode()和equals()方法
LinkedHashSet集合
哈希表和链表实现了Set接口,具有可预测的迭代次序。
由链表保证元素有序,也就是说元素的存储和取出的顺序是一致的
由哈希表保证元素唯一,也就是说没有重复的元素
TreeSet集合 有序
没有带索引的方法,所以不能使用普通for循环遍历
由于是Set集合,所以不能包含重复的元素
A NavigableSet实现基于TreeMap 。 的元件使用其有序natural ordering ,或由Comparator集合创建时提供,这取决于所使用的构造方法。
*** TreeSet() 构造一个新的,空的树组,根据其元素的自然排序进行排序。
TreeSet(Collection<? extends E> c) 构造一个包含指定集合中的元素的新树集,根据其元素的 自然排序进行排序 。
**** TreeSet(Comparator<? super E> comparator) 构造一个新的,空的树集,根据指定的比较器进行排序。
TreeSet(SortedSet<E> s) 构造一个包含相同元素的新树,并使用与指定排序集相同的顺序。
自然排序Comparable
用TreeSet集合存储自定义对象,无参构造方法使用的是自然排序对元素进行排序的
自然排序,就是让元素所属的类实现Comparable接口,重写compareTo(To)方法
重写方法时,一定要注意排序规则必须按照要求的主要条件和次要条件来写。
比较器排序Comparator
用TreeSet集合存储自定义对象,带参数构造方法的使用时比较器排序对元素进行排序的
比较器排序,就是让集合构造方法接受Comparator的实现类对象,重写compare(To1,To2)方法
重写方法时,一定要注意排序规则必须按照要求的主要条件和次要条件来写。
泛型
定义格式:
<类型>:指定一种类型的格式,这里的类型可以看成是形参,可以指定多种
具体调用时候给定的类型可以看做是实参,并且实参的类型只能是引用类型的数据类型
好处:
把运行时的问题提前到了编译期间
避免了强制类型转换
Generic 泛型类
定义格式:
修饰符 class 类名<类型>{}
范例:public class Generic<T>{}
此处的T可以随便写为任意的标识,常见的如T、E、K、V等形式的参数常用于标识泛型
泛型方法
定义格式:修饰符<类型> 返回值类型 方法名(类型 变量名)
public<T> void show(T t){}
泛型接口
格式:修饰符 interface 接口名<类型>{}
fanli:public interface Generic<T>{}
类型通配符
为了标识各种泛型List的父类,可以使用类型通配符
类型通配符: <?>
List<?>:表示元素类型未知的list,元素可以匹配任何的类型
这种带通配符的List仅表示它是各种泛型List的父类,并不能把元素添加到其中
如果说不希望list<?>是任何泛型的父类,只希望它代表某一类的泛型list的父类,可以使用类型通配符的上限
类型通配符上限:<? extends 类型>
List<? extends Number>: 表示的类型是Number或者其子类
除了指定通配符的上限,还可以指定通配符的下限
类型通配符下限:<? super 类型>
List<? super Number>: 表示的类型是Number或者其父类
可变参数:
可变参数又称参数个数可变,用作方法的形参出现,那么方法参数个数就是可变的了
格式: 修饰符 返回值类型 方法名 (数据类型 ... 变量名)
public static int sum(int ...a){}
这里的变量其实就是一个数组
如果一个方法有多个参数,包含可变参数,可变参数要放在最后
可变参数的使用:
Arrays工具类中有一个静态的方法:
public static <T> List<T> asList(T... a)返回由指定数组支持的固定大小的列表。
返回的集合不能做增删操作,可以做修改操作
List接口中有一个静态方法:
public static<E> List<E> of(E ... elements):返回包含任意数量元素的不可变列表
返回的集合不能增删改操作
Set接口中有一个静态方法:
public static<E> Set<E> of(E ... elements):返回一个包含任意数量元素的不可变集合
给元素的时候,不能给重复的元素
返回的集合不能做增删操作,没有修改的方法
Map集合
Map集合的概述和使用:
Map集合概述:
Interface Map<K,V> 参数类型 K - 维护的键的类型 V - 映射值的类型
将键映射到值的对象。 不能包含重复的键; 每个键可以映射到最多一个值。
学号 姓名
创建Map集合的对象:
多态的方式
具体的实现类HashMap
public V put(K key, V value) 将指定的值与此映射中的指定键相关联。 如果先前包含了该键的映射,则替换旧值。
Map集合的基本功能:
put(K key, V value) 将指定的值与该映射中的指定键相关联(可选操作)。
remove(Object key) 如果存在(从可选的操作),从该地图中删除一个键的映射。
clear() 从该地图中删除所有的映射(可选操作)。
containsKey(Object key) 如果此映射包含指定键的映射,则返回 true 。
containsValue(Object value) 如果此地图将一个或多个键映射到指定的值,则返回 true 。
isEmpty() 如果此地图不包含键值映射,则返回 true 。
size() 返回此地图中键值映射的数量。
Map集合的获取功能:
get(Object key) 返回到指定键所映射的值,或 null如果此映射包含该键的映射
Set<K> keySet() 获取所有键的集合
Collection<V> values() 获取所有值得的集合
Map集合大获取功能
Map集合的遍历方式1:
获取所有键的集合,用keySet()方法实现
遍历键的集合,获取到每一个键,用增强for实现
根据键去找值,用get(Object key)方法实现
Map集合的遍历方式2:
获取所有键值对对象的集合
Set<Map.Entry<K,V>> entrySet()返回此中包含的映射的Set视图。
遍历键值对对象的集合,得到每一个键值对对象
用增强for实现,得到每一个Map.Entry
根据键值对对象获取键和值
getKey() 返回与此条目相对应的键。
V getValue() 返回与此条目相对应的值。
Collections
Collections概述和使用:
是针对集合操作的工具类
public class Collections extends Object此类仅由静态方法组合或返回集合。 它包含对集合进行操作的多态算法,
“包装器”,返回由指定集合支持的新集合,以及其他一些可能的和最终的。
常用方法: sort(List<T> list) 根据其元素的natural ordering对指定的列表进行排序。
reverse(List<?> list) 反转指定列表中元素的顺序。
shuffle(List<?> list, Random rnd) 使用指定的随机源随机排列指定的列表。
sort(List<T> list, Comparator<? super T> c) 根据指定的比较器引起的顺序对指定的列表进行排序
IO流
File
文件和目录路径名的抽象表示
文件和目录是可以通过FIle封装成对象的
对于File而言,其封装的并不是一个真正存在的文件,仅仅是一个路径名而已,它是可以存在的,
也可以是不存在的,将来是要通过具体的操作把这个路径的内容转换为具体存在的。
构造方法
File(File parent, String child) 从父抽象路径名和子路径名字符串创建新的 File实例。
File(String pathname) 通过将给定的路径名字符串转换为抽象路径名来创建新的 File实例。
File(String parent, String child) 从父路径名字符串和子路径名字符串创建新的 File实例。
File类创建功能
boolean createNewFile() 当且仅当具有该名称的文件尚不存在时,原子地创建一个由该抽象路径名命名的新的空文件。
如果文件不存在就创建文件,返回TRUE,存在,不创建,返回false
boolean mkdir() 创建由此抽象路径名命名的目录。
如果目录不存在就创建目录,返回TRUE,存在,不创建,返回false
boolean mkdirs() 创建由此抽象路径名命名的目录,包括任何必需但不存在的父目录。
如果目录不存在就创建目录,返回TRUE,存在,不创建,返回false
File类判断和获取功能
boolean isDirectory() 测试此抽象路径名表示的文件是否为目录。
boolean isFile() 测试此抽象路径名表示的文件是否为普通文件。
boolean exists() 测试此抽象路径名表示的文件或目录是否存在。
String getAbsolutePath() 返回此抽象路径名的绝对路径名字符串。
String getPath() 将此抽象路径名转换为路径名字符串。
String getName() 返回由此抽象路径名表示的文件或目录的名称。
String[] list() 返回一个字符串数组,命名由此抽象路径名表示的目录中的文件和目录
File[] listFiles() 返回一个抽象路径名数组,表示由该抽象路径名表示的目录中的文件。
File类的删除功能
boolean delete() 删除由此抽象路径名表示的文件或目录。
如果一个目录中有内容(目录、文件),不能直接删除,应该先删除目录中的内容,最后才能删除目录
File递归
递归概述:以编程的角度来看,递归指的是方法定义中调用方法本身的现象
解决问题的思路:
把一个复杂的问题层层转换为一个与原问题相似的规模较小的问题来求解,
递归策略只需要少量的程序就可以描述出解题过程所需要的多次重复计算。
递归解决问题要找到两个内容:
递归出口:否则就会出现内存溢出
递归规则:与原问题相似的规模较小的问题
字节流
IO流概述
IO:输入/输出(Input/Output)
流:是一种抽象的概念,是对数据传输的总称,也就是说数据在设备间的传输称为流,流的本质是数据传输。
IO流就是用来处理设备间数据传输问题的
常见的应用:文件复制、文件上传、文件下载
IO流分类
按照数据的流向
输入流:读数据
输出流:写数据
按照数据类型来分
字节流
字节输入流;字节输出流
字符流
字符输入流;字符输出流
一般来说,我们说的IO流的分类是按照数据类型来分的
如果数据通过window自带的记事本软件打开,我们还可以读懂里面的内容,就使用字符流,
否则使用字节流,如果不知道该使用那种类型的流,一般就使用字节流,
字节流
字节流写数据
字节流抽象基类
InputStream:这个抽象类是表示输入字节流的所有类的超类。
需要定义InputStream子类的应用InputStream必须始终提供一种返回输入的下一个字节的方法。
OutputStream:这个抽象类是表示字节输出流的所有类的超类。 输出流接收输出字节并将其发送到某个接收器。
需要定义OutputStream子类的应用OutputStream必须至少提供一个写入一个字节输出的方法。
子类名称特点:子类名都是以其父类名作为子类名的后缀
FileOutputStream:文件输出流是用于将数据写入到输出流File或一个FileDescriptor 。
FileOutputStream(String name) 创建文件输出流以指定的名称写入文件。
使用字节输出流写数据的步骤:
创建字节输出流对象(A:调用系统功能创建了文件B:创建了字节输出流对象 C:让字节输出流对象指向创建好的文件)
调用字节输出流对象的写数据方法
释放资源(关闭此文件输出流并释放与此流相关的任何系统资源)
构造方法
FileOutputStream(File file) 创建文件输出流以写入由指定的 File对象表示的文件。
FileOutputStream(String name) 创建文件输出流以指定的名称写入文件。
字节流写数据的三种方式
void write(byte[] b)将 b.length个字节从指定的字节数组写入此文件输出流。
void write(byte[] b, int off, int len) 将 len字节从位于偏移量 off的指定字节数组写入此文件输出流。
void write(int b)将指定的字节写入此文件输出流。
字节流写数据的两个小问题
字节流写数据如何实现换行?
写完数据加换行符
window:\r\n
linux:\n
mac:\r
字节流写数据如何实现追加写入?
public FileOutputStream(String name, boolean append) 创建文件输出流以指定的名称写入文件。
如果第二个参数是true ,则字节将写入文件的末尾而不是开头。
字节流写数据加异常处理
finally:在异常处理时提供finally块来执行所有的清除操作,比如说IO流中的释放资源
特点:被finally控制的语句一定会执行,除非JVM退出
字节流读数据
字节流读数据(一次读一个字节数据)
把fos3.txt文件中的内容读出来输出在控制台
FileInputStream从文件系统中的文件获取输入字节。
FileInputStream(String name)
通过打开与实际文件的连接来创建一个 FileInputStream ,该文件由文件系统中的路径名 name命名。
使用字节输入流读数据的步骤:
1、创建字节输出流对象
2、调用字节输出流对象的读数据方法
3、释放资源
字节流读数据(一次读一个字节数组数据)
需求:把文件fos3.txt中的内容读取出来输出在控制台
字节缓冲流
BufferedOutputStream 该类实现缓冲输出流。 通过设置这样的输出流,应用程序可以向底层输出流写入字节,而不必为写入的每个字节导致底层系统的调用
BufferedInputStream为另一个输入流添加了功能,即缓冲输入和支持mark和reset方法的功能。 当创建BufferedInputStream时,将创建一个内部缓冲区数组。 当从流中读取或跳过字节时,内部缓冲区将根据需要从所包含的输入流中重新填充,一次有多个字节。
构造方法:
字节缓冲输出流:BufferedOutputStream(OutputStream out)创建一个新的缓冲输出流,以将数据写入指定的底层输出流。
字节缓冲输入流:BufferedInputStream(InputStream in)创建一个 BufferedInputStream并保存其参数,输入流 in ,供以后使用。
为什么构造方法需要的是字节流,而不是具体的文件或者路径?
字节缓冲流仅仅提供缓冲区,而真正的读写数据还得依靠基本的字节流对象进行操作
字符流
为什么会出现字符流
由于字节流操作中文不是很方便,所以java提供了字符流
字符流 = 字节流 + 编码表
汉字在存储的时候,无论是选择那种编码存储,第一个字节都是负数
编码表
基础知识:
计算机中存储的信息都是用二进制数表示的,我们在屏幕上看到的都是二进制转换之后的结果
按照某种规则,将字符存储到计算机中,称为编码,反之,将存储在计算机中的二进制数按照某种
规则解析显示出来,称之为解码。按照什么编码存储就必须按照什么编码解析,这样子才能正确显示正确的文本,否则就会导致乱码的现象。
字符编码:就是一套自然语言的字符与二进制之间的对应规则A(65)
字符集:
是一个系统支持的所有字符的集合,包括国家文字,标点符号,图形符号,数字等
计算机要准确的存储和识别各种字符集符号,就需要进行字符编码,一套字符集必然至少要有一套字符编码,
常见的字符集有ASCII字符集,GBXXX字符集,Unicode字符集等
字符串中的编码解码问题
方法
编码:
byte[] getBytes():使用平台的默认字符集将该String编码为一系列字节,将结果存储到新的字节数组中
byte[] getBytes(String charseName):使用指定的字符集将该String编码为一系列字节,将结果存储到新的字节数组中
解码:
String(byte[] bytes):通过使用平台的默认字符集解码指定的字节数组来构造新的String
String(byte[] bytes,String charseName):通过指定的字符集解码指定的字节数组来构造新的String
字符流中的编码解码问题
字符流抽象基类
Reader:字符输入流的抽象类
Writer:字符输出流的抽象类
字符流中的编码解码问题相关的两个类
InputStreamReader
InputStreamReader是从字节流到字符流的桥:它读取字节,并使用指定的charset将其解码为字符 。 它使用的字符集可以由名称指定,也可以被明确指定,或者可以接受平台的默认字符集。
OutputStreamWriter
OutputStreamWriter是字符的桥梁流以字节流:向其写入的字符编码成使用指定的字节charset 。 它使用的字符集可以由名称指定,也可以被明确指定,或者可以接受平台的默认字符集
构造方法
InputStreamReader(InputStream in) 创建一个使用默认字符集的InputStreamReader。
InputStreamReader(InputStream in, Charset cs) 创建一个使用给定字符集的InputStreamReader。
OutputStreamWriter(OutputStream out) 创建一个使用默认字符编码的OutputStreamWriter。
OutputStreamWriter(OutputStream out, Charset cs)创建一个使用给定字符集的OutputStreamWriter。
字符流写数据的五种方式
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) 写一个字符串的一部分
void flush() 刷新流。还可以继续写数据
void close() 关闭流,先刷新。关闭之前刷新,关闭之后不能写数据
字符流读数据的两种方式
int read() 读一个字符数据
int read(cahr[] cbuf) 一次读一个字符数组数据
FileReader:用于读取字符文件的便捷类
FileReader(String fileName) 创建一个新的 FileReader ,给定要读取的文件的名称。
FileWriter:用于写入字符文件的便捷类
FileWriter(String fileName) 构造一个给定文件名的FileWriter对象。
字符缓冲流
BufferedReader
从字符输入流读取文本,缓冲字符,以提供字符,数组和行的高效读取。
可以指定缓冲区大小,或者可以使用默认大小。 默认值足够大,可用于大多数用途。
BufferedWriter
将文本写入字符输出流,缓冲字符,以提供单个字符,数组和字符串的高效写入。
可以指定缓冲区大小,或者可以接受默认大小。 默认值足够大,可用于大多数用途。
构造方法:
BufferedReader(Reader in) 创建使用默认大小的输入缓冲区的缓冲字符输入流。
BufferedWriter(Writer out) 创建使用默认大小的输出缓冲区的缓冲字符输出流。
字符缓冲流的特有功能
BufferedWirter:
void newLine() 写一行行分隔符。行分隔字符串由系统属性定义。
BufferedReader:
String readLine() 读一行文字。结果包含行的内容的字符串,不包括任何行终止字符,如果流的结尾已经到达,则为null。
IO流小结
字节流:
字节输入流InputStream 抽象类
读数据
int read()一次读取一个字节
int read(byte[] bytes) 一次读取一个字节数组
FileInputStream
BufferedInputStream
字节输出流OutputStream
写数据
void write(int by) 一次写一个字节
void write(byte[] bytes,int index,int len) 一次写一个字节数组的一部分
FileOutputStream
BufferedOutputStream
字节流可以复制任意的文件数据,有四种方式,一般采用字节缓冲流一次读写一个字节数组的方式
字符流:
字符输入流Reader 抽象类
读数据
int read()一次读取一个字符
int read(byte[] bytes) 一次读取一个字符数组
InputStreamReader
FileReader
BufferedReader
String readline() 一次读取一个字符串
字符输出流Writer
写数据
void write(int by) 一次写一个字符
void write(byte[] bytes,int index,int len) 一次写一个字符数组的一部分
OutputStreamWriter
FileWriter
BufferedWriter
void newLine() 写一个换行符
void write(String line) 一次写一个字符串
涉及到编码问题就必须使用InputStreamReader和OutputStreamWriter
字符流只能复制文本文件,有五种方式,一般采用字符缓冲流的特有功能
特殊操作流
标准输入输出流
System类中有两个静态的成员变量:
in
public static final InputStream in“标准”输入流。 该流已经打开,准备提供输入数据。 通常,该流对应于键盘输入或由主机环境或用户指定的另一个输入源。
out
public static final PrintStream out“标准”输出流。 此流已经打开并准备好接受输出数据。 通常,此流对应于显示输出或由主机环境或用户指定的另一个输出目标。
自己实现键盘录入数据:
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
自己实现键盘录入数据太麻烦了 ,所以java就提供了一个供我们使用的类
Scanner scanner = new Scanner(System.in);
System.out的本质是一个字节输出流
PrintStream ps = System.out;
PrintStream类有的方法,System.out都可以使用
打印流
字节打印流:PrintStream
字符打印流:PrintWriter
打印流的特点:
只负责输出数据,不负责读取数据
有自己的特有方法
字节打印流
PrintStream(String fileName):使用指定的文件名创建新的打印流
使用继承父类的方法写数据,查看的时候会转码,使用自己特有的方法写数据,查看的时候数据原样输出
字符打印流
ntWriterPri(String fileName)使用指定的文件名创建一个新的PrintWriter,而不需要自动执行行刷新。
PrintWriter(Writer out, boolean autoFlush) 创建一个新的PrintWriter。
out - 字符输出流
autoFlush - 一个布尔值 如果为真,则println,printf,或format方法将刷新输出缓冲区
对象序列化流
对象序列化:就是将对象保存到磁盘中,或者在网络中传输对象
这种机制就是使用一个字节序列表示一个对象,该自己序列包含:对象的类型,对象的数据和对象中存储的属性等信息
字节序列化写到文件之后,相当于文件中持久保存了一个对象的信息
反之,该字节序列还可以从文件中读取出来,重构对象,对它进行反序列化
要实现序列化和反序列化就要使用对象序列化流和对象反序列化流
对象序列化流:ObjectOutputStream
对象反序列化流:ObjectInputStream
对象序列化流:ObjectOutputStream
将Java对象的原始数据类型和图形写入OutputStream。
可以使用ObjectInputStream读取(重构)对象。 可以通过使用流的文件来实现对象的持久存储。 如果流是网络套接字流,则可以在另一个主机上或另一个进程中重构对象。
构造方法:
ObjectOutputStream(OutputStream out) 创建一个写入指定的OutputStream的ObjectOutputStream。
序列化对象的方法:
void writeObject(Object obj) 将指定的对象写入ObjectOutputStream。
一个对象想要被序列化,该对象所属的类必须实现Serializable接口
Serializable是一个接口标记,实现该接口,不需要重写任何方法
对象反序列化流:ObjectInputStream
反序列化先前使用ObjectOutputStream编写的原始数据和对象。
构造方法:
ObjectInputStream(InputStream in) 创建从指定的InputStream读取的ObjectInputStream。
反序列化对象的方法:
Object readObject() 从ObjectInputStream读取一个对象。
对象序列化流
用对象序列化流序列化了一个对象之后,加入我们修改了对象所属的类文件,读书去会不会出问题?
会出问题,抛出异常InvalidClassException
InvalidClassException:
当序列化运行时检测到类中的以下问题之一时抛出。
类的串行版本与从流中读取的类描述符的类型不匹配
该类包含未知的数据类型
该类没有可访问的无参数构造函数
如何解决这样子的问题呢?
给对象所属的类加一个serialVersionUID
给对象所属的类加一个值:static final long serialVersionUID = 42L;
如果一个对象中的某个成员变量的值不想被序列化,又该如何实现呢?
给成员变量加transient关键字修饰,该关键字标记的成员变量不参与序列化过程
private transient int age; transient 使用关键字修饰,使得不被序列化
Properties
是一个Map体系的集合类
Properties可以保存到流中或者从流中加载
Properties作为Map集合的使用
Properties作为Map集合的特有方法:
Object setProperty(String key,String value) 设置集合的键和值,都是String类型,底层调用Hashtable方法put
String getProperty(Stirng key) 使用此属性列表中指定的键搜索属性
Set<String> stringPropertyNames() 从该属性列表中返回一个不可修改的键集,其中键及其对应的值是字符串
Properties 和IO流结合的方法
void load(InputStream inStream) 从输入字节流读取属性列表(键和元素对)。
void load(Reader reader) 以简单的线性格式从输入字符流读取属性列表(关键字和元素对)。
void store(OutputStream out, String comments) 将此属性列表(键和元素对)写入此 Properties表中,以适合于使用 load(InputStream)方法加载到 Properties表中的格式输出流。
void store(Writer writer, String comments) 将此属性列表(键和元素对)写入此 Properties表中,以适合使用 load(Reader)方法的格式输出到输出字符流。
案例:集合到文件
需求:把ArrayList集合中的数据写入到文本文件,要求:每一个字符串元素作为文件中的一行数据
思路:
创建ArrayList集合
往集合中存储字符串元素
创建字符缓冲输出流对象
遍历集合,得到每一个字符串数据
调用字符缓冲输出流对象的方法写数据
释放资源
ArrayListToTxtDemo 集合到文件
案例:文件到集合
需求:把文本文件中的数据读取到集合中,并遍历集合,要求:文件中每一行数据是一个集合元素
思路:
创建字符缓冲输入流对象
创建ArrayList集合对象
调用字符缓冲输入流对象的方法读数据
把读到的字符串数据存储到集合中
释放资源
遍历集合
TxtToArrayListDemo 文件到集合
案例:点名器
需求:我有一个文件里面存储了班级同学的名字,每一个姓名占一行,要求通过程序实现随机点名器
思路:
创建字符缓冲输入流对象
创建ArrayList集合对象
调用字符缓冲输入流对象的方法读数据
把读取到的字符串数据存储到集合中
释放资源
使用Random产生一个随机数,随机数的范围在[0,集合的长度]
把第六步产生的随机数作为索引到ArrayList集合中获取值
把第七步得到的数据输出在控制台
CallNameDemo 点名器
案例:集合到文件(改进版)
需求:把ArrayList集合中的学生数据写入到文本文件。要求:每一个学生对象的数据作为文件中的一行数据,
格式:学号,姓名,年龄,居住地 举例:gwg001,狗哥,18,洛阳
思路:
定义学生类
创建ArrayList集合
创建学生对象
把学生对象添加到集合中
创建字符缓冲输出流对象
遍历集合,得到每一个学生对象
把学生对象的数据拼接成指定格式的字符串
调用字符缓冲输出流对象的方法写数据
释放资源
多线程
实现多线程
进程:是正在运行的程序
是系统进行资源分配和调用的独立单位
每一个进程都有它字节的内存空间和系统资源
线程:是进程中的单个顺序控制流,是一条执行路径
单线程:一个进程如果只有一条执行路径,则称为单线程程序。
多线程:一个进程如果有多条执行路径,则称为多线程程序。
Thread 线程是程序中执行的线程。 Java虚拟机允许应用程序同时执行多个执行线程。
实现多线程的方式1:
一个是将一个类声明为Thread的子类。
这个子类应该重写Thread类的run方法 。
然后可以分配并启动子类的实例。
重写run方法,run()方法时封装线程执行的代码,直接调用,相当于普通方法的调用
start():启动线程,然后由JVM调用此线程的run方法
设置和获取线程名称
Thread类中设置和获取线程名称的方法:
void setName(String name) 将此线程的名称更改为等于参数 name 。
String getName() 返回此线程的名称。
通过构造方法可以设置线程名称
如何获取main()方法所在的线程名称呢?
static Thread currentThread() 返回对当前正在执行的线程对象的引用。
线程调度
线程调度有两种模型:
分时调度模型:所有的线程轮流使用CPU的使用权,平均分配每个线程占用CPU的时间片
抢占式调度模型:优先让优先级高的线程使用CPU,如果线程的优先级相同,那么会随机选择一个,优先级高的线程获取的CPU时间片相对多一些
java使用的是抢占式调度模型
Thread类中设置和获取线程优先级的方法:
public final getPriority():返回此线程的优先级
public final setPriority(int newPriority):更改此线程的优先级
线程默认优先级是5,线程优先级的范围是:1-10
线程优先级高仅仅表示线程获取CPU时间片的几率高,但是要在次数比较多,或者多次运行的时候才能看到想要的效果
线程控制
static void sleep(long millis) 使当前正在执行的线程以指定的毫秒数暂停(暂时停止执行),具体取决于系统定时器和调度程序的精度和准确性。
void join() 等待这个线程死亡。
void setDaemon(boolean on) 将此线程标记为 daemon线程或用户线程。将此线程标记为守护线程,当运行的线程都是守护线程时,java虚拟机将退出。
线程的生命周期
新建->就绪->运行—>死亡
就绪<-阻塞<-运行->就绪
实现多线程的方式2:
实现Runnable接口
创建一个线程是声明实现类Runnable接口。
那个类然后实现了run方法。 然后可以分配类的实例,
在创建Thread时作为参数传递,并启动。
多线程的实现有两种方式:
继承Thread类
实现Runnable接口
相比继承Thread类,实现Runnable接口的好处:
避免了java单继承的局限性
适合多个相同程序的代码去处理同一个资源的情况,把线程和程序的代码、数据有效分离,较好的体现了面向对象的设计思想
线程同步
多线程数据安全问题:
是否是多线程环境
是否共享数据
是否有多条语句操作共享数据
同步代码块
锁多条语句操作共享数据,可以使用同步代码块实现
格式:synchronized(任意对象){
多条语句操作共享数据的代码
}
synchronized(任意对象):相当于给代码块加了锁,任意对象可以看成就是一把锁
同步的好处和弊端:
好处:解决了多线程的数据安全问题
弊端:当线程很多事,因为每个线程都会去判断同步上的锁,这是很耗费资源的,无形中会降低程序的运行效率
同步方法
就是把synchronized关键字加到方法上
格式:
修饰符synchronized 返回值类型 方法名(参数类型){}
同步对象方法的锁是什么呢?
this
同步静态方法
就是把synchronized关键字加到静态方法上
格式:
修饰符 static synchronized 返回值类型 方法名(参数类型){}
同步静态方法的锁是什么呢?
类名.class
线程安全的类
StringBuffer
线程安全,可变的字符序列。
从版本JDK 5开始,这个类别已经被一个等级类补充了,这个类被设计为使用一个线程StringBuilder 。通常应该使用StringBuilder类,因为它支持所有相同的操作,但它更快,因为它不执行同步。
Vector
从Java 2平台v1.2,这个类被改造为实现List接口,使其成为成员Java Collections Framework 。 与新集合实现不同, Vector是同步的。 如果不需要线程安全的实现,建议使用ArrayList代替Vector 。
Hashtable
该类实现了一个哈希表,它将键映射到值。 任何非null对象都可以用作键值或值。
从Java 2平台v1.2,这个类被改造为实现Map接口,使其成为成员Java Collections Framework 。 与新的集合实现不同, Hashtable是同步的。 如果不需要线程安全的实现,建议使用HashMap代替Hashtable 。 如果需要线程安全的并发实现,那么建议使用ConcurrentHashMap代替Hashtable 。
一般使用StringBuffer
Vector被替代了
Hashtable被替代了
Lock锁
虽然我们可以理解同步代码块和同步方法的锁对象,但是我们并没有直接看到在哪里加上了锁,在哪里释放了锁,为了更清晰的表达如何加锁和释放锁,
JDK5以后提供了一个新的锁对象Lock
Lock实现提供比使用synchronized方法和语句可以获得的更广泛的锁定操作。
Lock中提供了获得锁的释放锁的方法
void lock() 获得锁。
void unlock() 释放锁。
Lock是接口不能直接实例化,这里采用它的实现类ReentrantLock来实例化。
ReentrantLock类的构造方法:
ReentrantLock() 创建一个 ReentrantLock的实例。
生产者消费者
生产者消费者模式是一个十分经典的多线程协作的模式,弄懂生产者消费者问题能够让我们对多线程编程的理解更加深刻。
包含两类线程:
一类是生产者线程用于生产数据
一类是消费者线程用于消费数据
为了解生产者和消费者的关系,通常会采用共享的数据区域,就像是一个仓库
生产者生产数据之后直接放置在共享数据区中,并不需要关心消费者的行为。
消费者只需要从共享数据区中去获取数据,并不需要关心生产者的行为。
生产者-->共享数据区域<-- 消费者
生产者消费者模式概述
为了体现生产者和消费过程中的等待和唤醒,java就提供了几个方法供我们使用,这个几个方法在Object类中。
Object类中的等待和唤醒方法
void wait() 导致当前线程等待,直到另一个线程调用该对象的 notify()方法或 notifyAll()方法。
void notify() 唤醒正在等待对象监视器的单个线程。
void notifyAll()唤醒正在等待对象监视器的所有线程。
网络编程
网络编程入门
网络编程:在网络通信协议下,实现网络互连的不同计算机上运行的程序间可以进行数据交换
计算机网络:
计算机网络系统就是利用通信设备和线路将地理位置不同、功能独立的多个计算机系统互联起来,
以功能完善的网络软件实现网络中资源共享和信息传递的系统。 通过计算机的互联,实现计算机之间的通信,
从而实现计算机系统之间的信息、软件和设备资源的共享以及协同工作等功能,其本质特征在于提供计算机
之间的各类资源的高度共享,实现便捷地交流信息和交换思想。
网络编程三要素
IP地址:要想让网络中的计算机能够相互通信,必须为每台计算机指定一个表示号,通过这个表示号来指定要接收数据的计算机和识别发送的计算机,
而IP地址就是这个标识号,也就是设备的标识。
网络中设备的唯一标识
IP地址分为两大类:
IPv4:网际协议版本4(英语:Internet Protocol version 4,IPv4),又称互联网通信协议第四版,是网际协议开发过程中的第四个修订版本,也是此协议第一个被广泛部署的版本。IPv4是互联网的核心,也是使用最广泛的网际协议版本,其后继版本为IPv6,直到2011年,IANA IPv4位址完全用尽时,IPv6仍处在部署的初期。
IPv4在IETF于1981年9月发布的 RFC 791 中被描述,此RFC替换了于1980年1月发布的 RFC 760。
IPv4是一种无连接的协议,操作在使用分组交换的链路层(如以太网)上。此协议会尽最大努力交付数据包,意即它不保证任何数据包均能送达目的地,也不保证所有数据包均按照正确的顺序无重复地到达。这些方面是由上层的传输协议(如传输控制协议)处理的。
32bit地址
IPv6:网际协议版本4(英语:Internet Protocol version 4,IPv4),又称互联网通信协议第四版,是网际协议开发过程中的第四个修订版本,也是此协议第一个被广泛部署的版本。IPv4是互联网的核心,也是使用最广泛的网际协议版本,其后继版本为IPv6,直到2011年,IANA IPv4位址完全用尽时,IPv6仍处在部署的初期。
IPv4在IETF于1981年9月发布的 RFC 791 中被描述,此RFC替换了于1980年1月发布的 RFC 760。
IPv4是一种无连接的协议,操作在使用分组交换的链路层(如以太网)上。此协议会尽最大努力交付数据包,意即它不保证任何数据包均能送达目的地,也不保证所有数据包均按照正确的顺序无重复地到达。这些方面是由上层的传输协议(如传输控制协议)处理的。
128位地址长度
ipconfig:查看本机IP
ping ip地址:查看网络是否连通
特殊的ip地址:127.0.0.1:回送地址,可以代表本机地址,一般用来测试使用
InetAddress的使用
为了方便我们对ip地址的获取和操作,java提供了一个类InetAddress供我们使用
此类表示Internet协议(IP)地址。
常用方法:
static InetAddress getByName(String host) 确定主机名称的IP地址。
String getHostAddress() 返回文本显示中的IP地址字符串。
String getHostName() 获取此IP地址的主机名。
端口:网络的通信,本质上就是两个应用程序的通信,每台计算机都有很多的应用程序,那么在网络通信时,如何区分这些应用程序呢?
那么端口号就可以唯一标识设备中的应用程序了,也就是应用程序的标识。
端口,设备上应用程序的唯一标识
端口号:用两个字节标识的整数,他的取值范围是0~65535,其中0~1023之间的端口号用于一些知名的网络服务和应用,
普通的应用程序需要使用1024以上的端口号,如果端口号被另一个服务或者应用占用,会导致当前程序启动失败
协议:协议,网络协议的简称,网络协议是通信计算机双方必须共同遵从的一组约定。如怎么样建立连接、怎么样互相识别等。
只有遵守这个约定,计算机之间才能相互通信交流。它的三要素是:语法、语义、时序。
为了使数据在网络上从源到达目的,网络通信的参与方必须遵循相同的规则,这套规则称为协议(protocol),它最终体现为在网络上传输的数据包的格式。
UDP协议和TCP协议
计算机网络中,连接和通信的规则被称为通信协议
UDP协议
用户数据报协议(User Datagram Protocol)
UDP是无连接通信协议,即在数据传输时,数据的发送端和接收端不建立逻辑连接。
简单来说,当一台计算机向另一台计算机发送数据时,发送端不会确认接收端是否存在,
就会发出数据,同样接受端在收到数据时,也不会向发送端反馈是否收到数据。
由于使用UDP协议消耗资源小,通信效率高,所以通常都会使用用于音频,视频和普通数据的传输
TCP协议
传输控制协议(Transmission Control Protocol)
TCP协议是面向连接的通信协议,即传输数据之前,在发送端和接收端建立逻辑连接,
然后再传输数据,它提供了两台计算机之间可靠无差错的数据传输,在TCP连接中必须要明确客户端与服务器端,
由客户端向服务端发出连接请求,每次连接的创建都需要经过“三次握手”
三次握手:在TCP协议中,在发送数据的准备阶段,客户端与服务器端之间的三次交互,以保证连接的可靠。
第一次握手:客户端向服务器端发送连接请求,等待服务器确认
第二次握手,服务器端向客户端回送一个响应,通知客户端收到了连接请求
第三次握手,客户端再次向服务器端发送确认信息,确认连接
完成三次握手,连接建立,客户端和服务器端就可以开始进行数据传输了,由于这种面向连接的特性,
TCP协议可以保证传输数据的安全,所以应用十分广泛,上传文件、下载文件、浏览网页等
UDP通信程序
UDP通信原理
UDP协议是一种不可靠的网络协议,它在通信的两端各建立一个Socket对象,但是这两个Socket只是发送,接受数据的对象,因此对于急于UDP协议的通信双方而言,没有所谓的客户端和服务器的概念
Java提供了DatagramSocket类,作为基于UDP协议的Socket
UDP发送数据
发送数据的步骤
创建发送端的Socket对象(DatagramSocket)
创建数据,并把数据打包
调用DatagramSocket对象的方法发送数据
关闭发送端
UDP接收数据
接收数据的步骤
创建接收端的Socket对象(DatagramSocket)
创建一个数据包,用于接收数据
调用DatagramSocket对象的方法接收数据
解析数据,并把数据在控制台显示
关闭接收端
TCP通信程序
TCP通信原理
TCP通信协议是一种可靠的网络协议,它在通信的两端各建立一个Socket对象,从而在通信的两端形成网络虚拟链路,一旦建立了
虚拟的网络链路,两端的程序就可以通过虚拟链路进行通信
java对基于TCP协议的网络提供了良好的封装,使用Socket对象来代表两端的通信端口,并通过Socket产生IO流来进行网络通信
java为客户端提供了Socket类,为服务器端提供了ServerSocket类
TCP发送数据
发送数据的步骤
创建客户端的Socket对象(Socket)
获取数据流,写数据
释放资源
TCP接收数据
接收数据的步骤
创建服务器端的Socket对象(ServerSocket)
监听客户端连接,返回一个Socket对象
获取输入流,读数据,并把数据显示在控制台
释放资源