1.java语言的特点:
1.简单性:java语言是纯面向对象语言,语法简单,单继承,没有指针,还会自动收集内存垃圾
2.面向对象性:所有元素都要通过类和对象来访问,万物皆对象
3.平台无关性:跨平台,只需要开发一次就可以在不同平台(也可以说操作系统)上运行
4.分布式:支持网络,可多个客户端相互调用
5.健壮性:java编写的程序很多方面具有可靠性,java编译器编译期间就能检测出其他语言仅在运行中才能检测出的问题(java会检查程序在编码和运行时的错误,并消除错误)
5.安全性:删除了指针操作,有效避免了用户对内存的非法操作,然后java程序代码要经过很多测试步骤才能运行,减少了出现损害系统平台的行为,最后就是java可以编写防病毒和防修改系统
6.可移植性:java的类库提供了针对不同平台的接口,所以这些类库可以被移植
7.多线程性:java应用程序可以同时执行多项任务,而且相应的同步机制保证了不同线程能够共享数据
8.动态性:可以动态调整库中的方法和增加变量,不影响客户端
2.关键字
关键字所有字母都是小写,被赋予特殊含义的字符串
1.this关键字
this关键字代表当前对象,就近原则,构造方法里的this.s=s;表示这个s是构造方法里的s而不是成员变量里的s
2.static关键字
static被称为静态,可以用来修饰类的属性,方法,代码块,内部类
·随类加载而加载
·优先于对象存在
·修饰的成员被所有对象所共享
·修饰的方法可以直接被类调用,不可创建对象//可直接通过类名调用,不需要创建对象
3.final关键字(常量,不可变的)
用于声明方法 属性 类
属性:定义的时候就必须赋值 而且后期都不能改变
(声明时不赋值,就必须在构造方法中逐一赋值)
方法:子类中不可以重写
类:不能被定义为抽象类或是接口 也不能被继承
常常于static一起使用,原则是保证创建每一个对象的时候,final属性的值都是确定的,防止数据在方法体中被修改
4.super关键字
父类的 调用父类构造方法(必须写在第一行)访问父类成员变量 访问父类的方法
!!!不是创建父类对象,是将父类的信息加载到子类对象储存
3.标识符
用来命名的 给变量、方法、类、包
强制语法:大小写字母,数字,下划线,¥,但是不能数字开头,不能有空格,区分大小写(驼峰区分大小写userName,不区分大小写就这样写user_name)
包:包名全部小写xxxyyyzzz
类、接口:类名所有的首字母都大写XxxYyyZzz
方法、变量:第一个首字母小写,后面的首字母都大写,如xxxYyyZzz
常量名:所有字母都大写
包的命名规范:(全小写)
第一级:项目类型,com(商业),org(机构),gov(政府)等
第二级:项目所开发或者运行的公司名称,如:oracle,sun,huawei等
第三级 :项目的名称,如:bcms,oa,erp,cms等
第四级:项目模块的名称,如bean,action,exception等
4.java基本数据类型
Java中每一种数据都定义了明确的具体数据类型,决定了内存中分配不同的大小内存空间
整数类型:上次考试问到,没答出来
浮点型:
float 单精度 四个字节
double 双精度 八个字节 默认double型,声明float可以在常量后加f或F
5.基本数据类型转换
类型转换:byte short char(不分大小)--->int---->long--->float---->double
小转大默认转换,大容量转小容量需强制转换,丢失精度(装不完总要丢掉一点)
6.编码
最早是美国制定类一套字符编码,但是只是对于英文字符与二进制的关系作出统一规定(世界上又不只有英文)所以面向全世界的unicode编码问世,支持所以语言。
UTF-8是Unicode中具体的实现方式:可以根据字符大小进行变更,一个英文字母在UTF-8中占一个字节,一个中文占3个字节
7.数组
存储数据类型相同的数据,空间连续,长度给定则不可变,本身是引用数据类型,即是对象,可以存基本数据类型,也可以存引用数据类型
1.数组的创建:
int[] a = new int[10];
int[] a = new int[]{1, 2, 3, 4, 5};
int[] a = {1, 2, 3, 4, 5};
二维数组:
int a = new int{{1,2,3},{1,2,3},{1,2,3}};
int [] [] b = {{1,2,3},{1,2,3},{1,2,3}};
int c = new int[3] [5];
2.数组的迭代:
1)for循环
for( int i;i<数组.length;i++){
sout(数组[i]);
}
2)增强for循环
for(数组元素的类型 临时变量名字 :要遍历的数组的名字){
System.out.println(临时变量名字 );
}
3)二维数组的迭代
int [][] b1 = new int [][]{{1,2},{3,4}};
for(int i =0;i<b1.length;i++){
for(int j =0;j<b1[i].length;j++){
System.out.print(b1[i][j]);
}
}
3.数组的排序:
1)冒泡排序
算法思想:拿两个相邻的位置比
int[] arr={5,3,4,2,1};
for(int i=0;i<arr.length-1;i++) {//循环次数
for(int j=0;j<arr.length-1-i;j++) {//交换次数
•
if (arr[j] > arr[j + 1]) {
•
int max = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = max;
}
}
}
System.out.println(Arrays.toString(arr));
2)选择排序
算法思想:取一个数与后面的数逐个
int[] arr = {5, 3, 2, 4, 1};
for (int i = 0; i < arr.length - 1; i++) {//控制循环次数 拿出比较基数
for (int j = i + 1; j < arr.length; j++) {//基数与后面比较次数
if (arr[i] > arr[j]) {
int max = arr[i];
arr[i] = arr[j];
arr[j] = max;
}
}
}
System.out.println(Arrays.toString(arr));
8.面向对象(OOP)
1.什么是面向对象?
对现实中的事物进行分类 创建出类 在类中定义功能(方法) 创建出具体对象,让对象做具体的事
面向对象是一种编程思想
案例:
总结:面向对象无法取代面向过程,他们相辅相成,面向对象关注于从宏观上把握事物之间的关系,在具体到如何实现某个细节时,仍然采用面向过程的思维方式。
2.面向对象的特征(好处)
封装继承多态
1)封装
隐藏,私有化
每个类都会有属性和方法,封装就是把一些属性和方法隐藏起来,不允许外部直接访问,但可以通过该类提供的(set/get)方法来实现对隐藏信息的操作和访问
好处:减少耦合,方便修改实现代码,不用修改调用的程序片段
//set()方法
private String name;
public void setName(String name){
if(条件){
this.name=name;
}
}
//get()方法
public String getName(){
return name;
}
}
2)继承
(extends)(java是单继承)
实现代码重用 提高代码可扩展性
继承是从已有的类中派生出来的新类,新类吸收了已有类的行为和属性(除了私有的 父亲也有自己的隐私)还可以扩展自己的新能力
3)多态
同一种事物的不同表现状态
同一个接口,使用不同实现,而执行不同操作
多态三个必要条件:继承、重写、父类引用指向子类对象 Animal a=new dog();
3.java中对象与引用概念(结合指针概念)
存储器中基本单位是Byte字节,最小单位是bit位,Byte=8bit,信息转换成01代码以字节存储,然后有地址概念,也就是c语言中的指针,在java中删除指针概念,引入引用概念。
对象:万物皆对象
创建对象:对象复制了一份成员变量,对象人手一份。对象调方法,是将方法的地址值传给对象。
Student s=new Student();//s拿到的是对象的引用(地址) 不是对象本身,就像指针拿到的是数据的地址值,也不是数据本身
对象在堆存,张三李四
引用在栈存,Student zs,Student ls...
ls.showInfo();
李四的引用调用showInfo()方法
4.调方法时参数传递:值传递(参数是基本类型),引用传递(参数是引用类型)
值传递:形参的改变不影响实参
引用传递:实参就是对象,实参传递的就是对象的引用,也就是地址,调用时形参和实参指向同一个地址,所以形参的改变会影响实参
9.抽象类和抽象方法abstract
抽象类中可以没有抽象方法,有抽象方法的一定是抽象类
抽象类不能创建对象 但成员变量 成员方法 构造方法依然存在
(为什么不能创建对象却有构造方法?构造方法的作用两个1初始化对象2初始化实例变量,虽然没有对象但有变量啊)
抽象类就是用来做父类的给子类继承的,没有子类继承就没有意义
如果一个类继承了抽象类就必须实现它的抽象方法,不然就也定义为抽象类
10.接口
定义interface 使用implements
接口的意义:java是单继承的,接口可以实现多继承的逻辑
从本质上讲:接口是一种特殊的抽象类,有抽象方法(就是功能定义,谁想要这个功能就接这个接口 自己重写)
[访问修饰符] class 子类名 extends 父类名 implements 接口1,接口2...{ }
接口的特征:
接口是隐式抽象,不需要abstract
接口不能实例化对象,无构造方法
接口的属性默认为public static final
接口不是被类继承 而是被实现
类似抽象类,当一个类实现接口的时候,类要实现接口中所有的抽象方法,否则类必须声明为抽象类
11.常用类--基本数据类型包装类
基本数据类型:
byte short int long float double char boolean
基本数据类型 没有面向对象的使用方法,所以java为每种基本类型定义了一个类,来表示基本数据类型,这个类叫做包装类(int 是基本类型 Integer是引用类型)(继承number类)
1.Integer类
以Integer类为例(其他几个包装类也差不多)
常用操作:用于基本数据类型与字符串之间的转换
包装类有max min 比较 构造 转换等方法
max()
min()
equals()//比较内容
compareTo()//比较前后两个字母的asc码的差值 略略略 不大懂
构造方法:
Integer s1=new Integer(123)
Integer s2=new Integer("123")
int和String类型相互转换:
1.int转成String
int i=100;
sout(String.valueof(i))
2.String转成int
String s="123";
sout(Integer.parselnt(s));
3.将Integer转为String类型
.toString()
进制转换方法:
Integer.toBinaryString(十进制数)//转二进制
Integer.toOctalString(十进制数)//转八进制
Integer.toHexString(十进制数)//转十六进制
或者
十进制转r进制(推荐):
Integer.toString(十进制数,进制)
2.Object类
Object类是所有Java类的祖先(根基类)
类中未声明extends关键词指明其类的,都默认为Object类
1.Object类提供的方法:
1)toString方法【获取对象信息】
【如果直接使用对象输出,默认输出的是对象在堆的地址,重写toString()输出该对象的内容】
Object类已经定义了toString方法,用户可以根据自定义重写。
重写:
@Override
public String toString(){
return "名字:"+this.name;
}
2)equals()方法【比较对象内容】
【一个对象 直接用equals(),两个对象默认比较的就是对象在堆的内存地址,需要重写equals()方法】
== 基本类型比较的是值,引用类型比较的是地址
equals 比较对象是否相等 返回true/false
重写:重写equals()方法必须重写hashCode()方法
如成员变量
String name;
int age;
@Override
public boolean equals(Object obj){
//判断是否是同一对象
if(this==obj)
return true;
//判断是否为空
if(obj==null)
return false;
//判断是否属于同一个类
if(getClass()!=obj.getClass())
return false;
//如果类型相同,比较内容
Person other=(Person)obj;
return Objects.equals(name,other.name)&&Objects.equals(age,other.age);
}
@Override
public int hashCode(){
return Object.hash(name,age);
}
3.Arrays类
常用方法:
1.equals方法:比较两个数组是否相等
2.toString()方法:返回指定数组内容的字符串表示形式
数组一般都是循环遍历输出的 但Arrays.toString(数组名)可以直接输出一个数组
3.sort方法:数组排序
数组一般都是循环遍历输出的 但Arrays.toString(数组名)可以直接输出一个数组
4.binarySearch:使用二分法搜索指定数组
二分法查找之前要先给数组排好序
如果key在数组中,则返回搜索值的索引;否则返回-7
5.Arrays.copyof(原数组名,新长度) : 动态数组扩容 拷贝
返回一个新数组,并将原数组复制到新数组
4.String/StringBuffer/StringBuilder类
1.String类
1)equals()方法 判断字符串内容是否相等
2)equalsIgnoreCase()
判断字符串内容是否相等 忽视大小写 如验证码不区分大小写
3)compareTo()
按照字典顺序比较字符串大小 相同返回0;不同则返回不同的那个字符他们的ASCII的差
4)contains()
判断前面的字符串是否包含后面的字符串
5)length()返回字符串的长度
6)toCharArray()将字符串转换成char[]数组,并返回
7)valueOf()将任何类型转换成字符串
8)还有截取substring(开始,结束);小写转大写toLowerCase();大写转小写toUpperCase();删除字符串里头部尾部空格空格trim();replace(" +","")删除所有空格;替换功能replace("a","C")//把a替换成C
2.StringBuffer类(缓冲区 可变)
当向StringBuffer中添加内容时,是将内容添加到底层的数组中,当数组装满时,会创建一个新数组,将新数组的地址给基层数组,StringBuffer对象是不会改变
常用方法
1)添加功能
append(" ") //附加 在末尾
insert(在哪个位置插入,“插入的字符串”) //插入
2) 删除功能
deleteCharAt(要删除的位置)
public StringBuffer delete(int start,int end) //包含开始不包涵结束 删除一段
3)反转功能
public StringBuffer reverse()//逆序
4) 截取功能 substring
3.StringBuilder类(缓冲区 可变 多线程不安全)
StringBuilder类功能和StringBuffer功能完全一致, StringBuffer是线程安全的
StringBuffer 有synchronized关键字 加锁了 得排队 一个一个访问 安全
多线程访问同一个东西 才会出现安全问题
5.Math/Random类
1.Math类
怎么用?Math.方法名()
sin正弦 cos余弦 tan正切 sqrt绝对值 pow乘方 round四舍五入
max最大值 min最小值
random() 返回一个 0.0 到 1.0 的随机数 (乘个10就是0到10的随机数)(不是整数,一般不用)
2.Random类:
专门用来生成随机数的 可以指定int boolean float类型的随机数 指定范围
6.Data/Calender/SimpleDateFormat类
1.Data类(基本不用了 被Calender类替代)
2.Calendar类
Calendeer类是一个抽象类,创建对象过程对于程序员来说是透明的,只需要使用getInstance方法创建即可 不需要new
Calendar c=Calendar.getInstance();
然后c.get()
3.SimpleDateFormat类(日期格式化类)
字符串转日期
12.异常
1.异常概念 体系
Error:java虚拟机无法解决的严重问题,一旦程序运行时出现错误 程序是无法解决的
Exception:程序运行时出现不正常情况,异常处理机制可以处理 如越界
2.throws和thhrow
throws不处理 向上抛出
throw显示抛出异常 抛出的是异常类实例化对象
1.throws
定义一个方法的时候可以使用throws关键字声明(表示可能出现的异常),表示此方法 不处理异常,而交给方法调用处进行处理,所以调用时必须处理声明的异常,要么使用try-catch处理掉,要么继续使用throws声明向上抛出去。
public void test throws 异常1,异常2,异常3{
}
2.throw
public static void someMethod() {
if (1==1) {
throw new RuntimeException("错误原因(实例化对象 就是getMessage();要输出的内容)"); //如何调用?catch{System.out.println(e.getMessage())}
}
}
3.异常处理
try、catch、 finally、throw、throws
基本语法:
try{
可能会发生异常的代码
}catch(异常类型 引用名 如e){
e.printStackTrace();//打印异常信息
异常处理代码
}finally{
必须执行代码
}
4.自定义异常类
(理解不够深入 后续还得深入理解)
自定义异常就是自己定义的异常类,继承异常类
自定义类:
package com.ffyc.javaexception;/* 我们可以根据自己的业务需要,开发自定义异常类 */
public class ScoreException extends Exception{
public ScoreException() {
super(); //继承父类Exception 不写也默认继承
}
public ScoreException(String message){//构造方法
super(message); //该子类继承父类的message
}
}
package com.ffyc.javaexception;
public class Demo7 {
public static void main(String[] args) {
try {
test(101);
} catch (ScoreException e) {
e.printStackTrace();//打印异常信息
System.out.println(e.getMessage());
}
}
public static char test(int score) throws ScoreException {
if(score<0||score>100){
// 不合法分数,没必要继续向下执行,此处可以使用throw直接在方法中抛出一个异常对象.
throw new ScoreException("不合法的分数");
}
if(score>=90){
return 'A';
}else{
return 'B';
}
}
}
13.集合
1.集合的概念
集合就是容器 存储一组数组 就像数组 数组也是容器 但是数组指定长度 不能改变 但数据量是会改变的 存储结构也会有特殊要求(增删多---链表结构;查询多---数组结构) 所以就有了集合类
集合就是容器(集合长度可变 )
操作:增删改查
2.集合的体系
单列集合Collection、双列集合Map
3.Collection接口
ArraysList--->数组: 连续空间 查询快 从中间添加删除效率低
查询快 增删慢 连续空间
LinkedList--->链表: 查询慢(从头或尾节点查)插入删除效率高
不需要连续空间 查询慢 但中间增删快(指针移动)
Vector--->数组:线程安全
总结:遍历 访问用ArrayList
插入 删除用LinkedList
比较器大家都有 其实就是sort方法 传入两个参数 调用compareTo方法 注意一定提前给定泛型 不然不乏识别两个参数的类型
以ArrayList为例 所以详细点 其他就不重复讲了 大家都有
1.Colletion接口常用方法:(单列集合共有的方法)
Collection<E> 集合类默认使用泛型,如果不写默认Object
为什么使用泛型?
--泛型 就是参数化类型 把数据类型当做形参传进去--
安全问题**:不明确类型会默认Object 装的时候爽了 一顿乱装 但存在向下转型问题 可以通过泛型在创建对象时把类型当做参数传进去 存一样的 <引用类型> 必须是引用类型 就是把类型固定
(1)常用方法:
add() 增加元素 addAll可以把另一个集合加进去
clear () 清空列表
contains()是否包含指定元素
c.retainAll(c1) c只保留与c1中共有的部分 保留交集 c1不变返回ture (如果两个集合一样 不变 返回false 两集合不变)
isEmpty() 是否为空
remove() 删除指定第一个元素 成功返回true 否则false
c.removeAll (c1) 删除c中c1有的内容 不是c1 c1不变
size() 实际往里面放的元素个数 长度
set(位置,替换后的元素)//替换指定位置元素
get(int index)//返回指定位置迭代元素 索引 循环遍历的时候用到
toArray();返回一个含此类集合所有元素的数组(即把集合以数组的形式返回)但是是Object类型的数组
1.ArrayList
( 数组结构 list值可重复 线程不安全 长度可变)
1.add添加元素过程
先检查底层数组能不能放下,可以就添加到末尾,不可以就会扩容。
2.扩容
grow(minCapacity)//扩容 扩容为原来1.5倍(数组复制 创建一个新数组 加进去 把原来的数组覆盖)
有扩容机制就要想到size()方法 考虑到数组长度和实际长度可能不等 size()表实际长度
3.比较器sort
比较器就是把一个接口传进去 然后重写方法 基本不用了
比较器再简化 函数式编程代替匿名内部类写法(把函数当参数丢进去):
集合名.sort((String o1,String o2)->{return o1.compareTo(o2);});//升序(o1.compareTo(o2)) 降序(o2.compareTo(o1))
4.list遍历迭代
三种(迭代器只能用在集合 集合提供了iterator方法可以直接调用)
5.三种遍历的区别:
·for循环支持在遍历过程中删除集合中的元素 注意元素前移,i++ 不然会漏掉一些元素
·增强for循环不支持遍历的时候删除元素
·迭代器
2.LinkedList
(链表结构 list可重复 线程不安全)
3.Vector
(后面说 前面还用不到)
(数组结构 线程安全)(实现了一个动态数组)
数组的缺点就是长度不可变 Vector可以实现自动增长的对象数组 弥补了这一缺点 使数组更灵活
4种构造方法:
1.创建一个默认向量:new Vector();//默认长度为10
2.创建一个指定大小的向量:new Vector(int size);//指定长度
3.创建一个指定大小 指定增量的向量:new Vector(int size,int incr)//增量表示向量每次增加多少个元素
4.创建一个包含集合的向量:new Vector(Collection c);
常用方法:add()//向向量末尾添加集合
add(int index,Object element)向指定位置插入集合
4.HashSet:无序 不重复
1.底层数据结构:哈希表+链表
2.支持add、remove等方法 不支持get() 即没有索引
3.遍历:两种遍历 增强for 还有迭代器 没有索引 用不了for循环
5.TreeSet:有序 升序 不重复
1.底层数据结构:二叉树(红黑树是一种自平衡的二叉树)
2.支持add、remove等方法 不支持get() 即没有索引
3.遍历:两种遍历 增强for 还有迭代器 没有索引 用不了for循环
4.Map接口
双列,即键值对存储,一一对应,不重复
key是无序的 且不重复;value也是无序的,值可重复
1.常用方法
增:put(key,value)
删:remove(key) 以键删值
查:get(key) 以键找值
containsKey(key) 有没有这个键 返回ture / false
containsValue(value) 有没有这个值 返回ture / false
2.Map实现类-->HashMap
底层实现:hash数组+链表+红黑树----->线程不安全 但效率高
可以储存null,key,value值
1.put()过程
添加一个元素时,会创建一个长度为16的哈希表(数组),哈希值取余16 计算出它的位置,如果要添加的位置有元素,就会生产一个链表,当链表的长度等于8(太长效率就会降低),就会将链表自动转换为红黑树,当数组内容占到12个位置(3/4)就会对数组扩容(为原来的2倍)
数组-->链表--->红黑树--->扩容
无重复:重复的值不是加不进去,而是加进去替换原来的值,即不重复就是覆盖
1.LinkedHashMap:
linkedhashmap最大的特点就是有序
是HashMap的子类,保证遍历map元素时,可以按照顺序实现遍历,若要频繁遍历,效率高于HashMap
2.Map实现类--->TreeMap
底层实现:红黑树
key自然排序(从小到大),实现排序遍历
3.Map的迭代
1.增强for循环:(不提倡)(拿出所有的key 对key进行遍历)
如:
Set<String> keyset=map.keySet();//拿到key
for(String key:keyset){
System.out.println(key+":"+map.get(key));
}
2.map迭代器:
把map里面所有key值都放到keyset里面去 得到一个Set集合 对Set进行遍历 就可以用增强for循环遍历
通过key找到对应的值
3.map专业(推荐 提倡)
Map底层为了方便遍历,将每个键值对存入一个Entry对象中,将多个Entry对象放到Set集合中
entry. 点就可以直接拿到 键和值 key和value想拿哪个拿哪个
总结:增强for循环和map迭代器都是遍历key,拿到值
专门的是直接拿到key和value,想拿哪个拿哪个
5.Collections类
集合也有自己的工具类,集合处理的工具类,都是给的 可以直接使用
工具工具 当然是有功能的:怎么用?--->Collections.方法名
1.排序----sort(集合名)
放一个函数表达式 o1.compareTo(o2)是升序,o2.compareTo(o1)是降序
2.交换(集合中的元素的位置)----swap(集合名,交换值1,交换值2)
3.最大----max() 返回最大值
4.二分查找(前提是有序集合)---sort(集合名)--->binarySearch(集合名,"查找元素") 如果查找不到返回负数 如果找到返回元素位置 第几位
5.填充(集合中所有元素替换成新元素)---fill(集合名,"新元素")
14.stream流和lambda表达式
1.概述
Java8的成功之处,新增stream 配合lambda表达式--->操作集合很便利
Stream将要处理的元素集合看作一种流,经过一系列流处理 得到我们想要的样子
2.特征
stream流不存储数据,只是按照规定计算,输出结果---->即不改变原始数据,stream流延迟性,只有调用终端操作,中间操作才会执行
3.lambda表达式的用法
lambda表达式是一个匿名函数,就是一段核心代码,本质是糖衣语法(用更少的代码实现同样的功能)
1.可以显式声明参数的类型,也可以由编译器自动从上下文推断参数的类型。
例如 (int a,int b)与 (a,b)相同。
代码更少,精简
4.stream流的用法
获取流+流操作
1.获取流
怎么获取?--->直接点嘛 是个集合就直接点 直接一个集合名.stream()
2.流操作
中间操作、终端操作-----怎么操作?---点
中间操作
sort()排序 o1-o2升序 o2-o1降序
filter()
max()
reduce 归约 求和 即把一个流缩成一个值
当然也可以求乘积:
distinct去重
count()返回集合长度 即元素个数
15.IO流
1.概述
io 即in/out 输入和输出 即应用程序与外部设备之间的数据传输
将程序运行时创建的对象输出到一个文件中(一般创建对象都是储存在内存中)为什么要将对象输出到文件中呢?----实现数据(对象信息)的持久保存,只要你不删----它就一直在
流(Stream):指一连串的数据(字符/字节)
Java.io包中最重要5个类和一个接口
5类:文件类File、字节输出流OutputStream、字节输入流InputStream、
1.1输入输出概念
输入input:读外部数据到程序(内存)中去
输出output:将程序(内存)数据输出到外部设备(磁盘 光盘)中
1.2 IO的分类
1.按数据流的方向:输入流输出流
2.按处理数据单位:字节流字符流
3.按功能:节点流处理流
2.File类
file对象可以表示文件或目录 还有一系列操作 就是说 可以通过file对象拿到一个文件(或者)文件夹 然后对其操作
主要操作有
但File对象无法操作文件的具体数据,即不能直接对文件进行读/写操作。
创建一个File对象 直接关联一个文件对象(不管存不存在 不存在createNewFile()创造就可以)
操作:没啥意思 大概了解 我可能是用不到什么
boolean exists():判断文件是否存在
boolean isFile() :判断是否为文件,是文件返回true,否则返回false
boolean isDirectory() 判断是否为文件夹
listFiles() 获取文件目录下所有子级
String getName() :获取文件名
long length():获取文件长度(字节数)
boolean createNewFile():创建一个文件
boolean delete():删除文件(劝你善良 也劝你不要用 回收站也找不回来)
3.输入流、输出流
4.字符流、字节流
1.字符流和字节流的区别:
字符流只能处理纯文本文件 如TXT文件
字节流就多了,视频图像 word ppt都可以
虽然字节流没有缓冲区 字符流有缓冲区 看似字符流效率高 但是缓冲字节流有缓冲区啊
2.
InputStream和OutputStream的子类都是字节流
字节输入流 FileInputStream
字节输出流 FileOutputStream
Reader和Writer的子类都是字符流
字符输入流 FileReader
字符输出流 FileWriter
5.Print流
单向的 只要输出 没有输入
即服务器后端向客户端响应信息 管你怎么样 我就是发
看吧 只要PrintWriter方法 没有PrintReader方法
原来没有aa文件 它会自动创建 然后写进去
16.网络编程
1.网络编程的概念
1.什么是计算机网络:
把分布在不同地理区域的具有独立功能的计算机(各种设备),通过通信设备与线路(网线)连接起来,有功能完善的软件实现资源共享和信息传递的系统
2.什么叫网络编程:
我们写的程序可以支持网络通信,实现程序与程序之间互联互通,计算机之间就可以联系起来
3.网络编程的目的:
直接或间接的通过网络协议与其他计算机进行通讯 即:把计算机通过网络协议联系起来
4.网络编程要素:
如何找到对方主机(电脑):IP
如何可靠高效的进行数据传输:通信协议
2.网络模型
1.理想模型:OSI参考模型
2.实际模型:TCP/IP模型
想象很美好 实际就接口层 ip层 运输层 最后应用层
1.运输层:TCP/UDP协议
如何高效稳定的传输信息 就需要大家都遵守规则 比如交规
1.TCP协议
可靠:需要建立连接--->s所以速度慢,还有释放连接
怎么建立连接:三次握手
怎么释放连接:四次挥手
2.UDP协议
不可靠:不需要建立连接 所以速度快
将数据封装成一个数据报 直接发送(丢)过去,然后就不管了 不管对方存不存在 对方接不接收
3.TCP协议的实现类:Socket
Socket类,创建时会3次握手建立连接 Socket创建客户端时需要ip和端口号,这个端口号是服务器的,ip也是服务器的ip
要建立连接先开启服务器 再开启客户端
1.客户端:Socket类
获取客户端的字节输出流对象OutputStream write发送数据(发送的内容,规定信息编码)
这样发:
怎么接:
获取客户端的字节输入流对象InputStream read接收数据
拿什么接:创建一个数组 接收的信息放到里面嘛
把byte数组转换成字符串 输出
2.服务器:ServerSocket类
服务器只需要端口号就可以 客户端根据端口号连接
4.DUP协议的实现类DatagramSocket、DatagramPacket
没有服务的概念 就只有发送端和接收端 不需要建立连接
平等的谁想法就发
DatagramSocket类创建对象 DatagramPacket数据报负责发送、接收信息
数据报:里面封装要发送的信息 ,接收者的IP地址和端口号
然后send()发送的是数据报
数据报接收信息只需要端口号,再创建个数组来接(存放)
模拟聊天:
发送端:可以一直发
package feifan;
import java.io.IOException;
import java.net.*;
public class send {
public static void main(String[] args) throws IOException {
//发送端
DatagramSocket datagramSocket=new DatagramSocket();//DatagramSocket既可以发送 也可以接收 不需要端口 数据报里有
byte[] b="季后赛".getBytes();
//创建数据报 封装数据
while (true){//一直发
DatagramPacket datagramPacket=new DatagramPacket(b,b.length, InetAddress.getByName("192.168.242.5"),9999);//数据报
datagramSocket.send(datagramPacket);
}
}
}
接收端:可以一直接
package feifan;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
public class receive {
public static void main(String[] args) throws IOException {
//接受信息
DatagramSocket datagramSocket=new DatagramSocket(9999);
byte[] b=new byte[1024];
while (true){//一直接
DatagramPacket datagramPacket=new DatagramPacket(b,b.length);//传进去一个数组 还有数组长度
datagramSocket.receive(datagramPacket);//接受数据到数据报中
String s=new String(b,0,datagramPacket.getLength());//实际长度
System.out.println(s);
}
}
}
后面线程了就可以把发送端写成一个线程 接收端写在一个线程 实现聊天功能
17.线程
1.程序、进程、线程
程序:(指一段静态的代码)为了完成特定任务,用计算机语言编写的一组指令的集合。
进程:正在执行的程序。从Windows操作系统来说,进程是操作系统进行资源分配的最小单位(从硬盘读取到内存中,资源分配)
线程:进程可进一步细化为线程,是一个进程内部的最小执行单位,是操作系统进行任务调度的最小单位,隶属于进程。
1.线程和进程的关系:
1)一个进程可以包含多个线程 ,线程属于进程,线程不能脱离进程而单独存在。
2)一个进程中至少包含一个线程(主线程)java的程序入口main()方法就是在主线程中被执行
3)主线程中可以创建并启动其他线程
4)一个进程内所有线程共享该进程所以内存资源
2.创建线程
两个方法创建线程:继承Thread类和实现Runnable接口
推荐用第二个:
因为java是单继承的,继承了Thread就不能再继承其他了,而实现Runnable还可以继承其他类,应对了java单继承的局限性 实现Runnable很容易实现资源共享
记得开启线程:创建线程的时候系统并不知道它的存在,start()调用的时候操作系统才会把它加到执行队列中
加了线程之后大家都是独立的 谁先执行都不一定
1.继承Thread类 重写run()方法
如果我们重写了run方法,当线程启动时,它将执行 run方法。
1.自定义的线程类继承Thread类
2.重写run()方法,编写线程执行体
3.创建线程对象,对象调用start()方法启动线程
2.实现Runnable接口 重写run()方法
不是线程 只是一个任务 把任务丢进创建的线程中去 然后启动
java.lang.Runnable接口中仅仅只有一个抽象方法run(),只需要实现其中的run方法即可实现线程
3.继承Thread和实现Runnable接口区别
继承Thread:线程代码存放Thread子类run方法中
实现Runnable接口:线程代码存放在接口的子类的run方法中
实现Runnable的好处:避免了单继承的局限性,多个线程可以共享同一个接口实现类的对象,非常适合多个相同线程来处理同一份资源 还可以继承其他类 修饰非静态方法 锁对象修饰的是this 就适合多个线程共享同一份资源的情况
3.Thread类中的方法
1.构造方法
2.setName("")线程命名
3.getName()获取名称
4.currentThread() 获得当前正在执行的线程对象 注意返回的是对象。
5.start()启动线程
6.setPriority(int newPriority)设置线程的优先级
7.sleep(long millis)让当前执行的线程休眠(暂停执行)休眠时间由millis(毫秒)来指定 休眠指定的时间
8.join()让等待线程结束 让其他线程阻塞 自己通行
9.yield() 让步 失去cpu使用权 重新排队 不是阻塞是重来
优先级:
计算机只有一个CPU,各个线程轮流获得CPU的使用权,才能执行任务
优先级用整数表示,取值范围是1~10,10是最高优先级,一般情况下,线程的默认优先级都是5,但是也可以通过setPriority和getPriority方法来设置或返回优先级
4.线程的状态
1.线程的5个生命周期
新建->就绪---(阻塞)--->运行->死亡
新建:创建的时候
就绪:start开启线程的时候,已具备了运行的条件,只是没分配到CPU资源
运行:获得cpu使用权
阻塞:让出CPU并临时中止自己的执行,进入阻塞状态(等待,而不是yield重新排队)
死亡:线程完成了它的全部工作或线程被提前强制性地中止或出现异常导致结束
5.线程的分类
分类:用户线程和守护线程
任何一个守护线程都是整个JVM中所有非守护线程的保姆,只要有一个非守护线程没有结束,守护线程全部工作,当最后一个非守护线程结束时,守护进程随JVM一同结束,守护线程的作用是为其他线程的运行提供遍历服务,典型应用GC(垃圾回收器)--一个称职的守护者
用户线程和守护线程两者几乎没有区别,唯一的不同之处就在于虚拟机的离开:如果用户线程已经全部退出运行了,只剩下守护线程存在了,虚拟机也就退出了。 因为没有了被守护者,守护线程也就没有工作可做了,也就没有继续运行程序的必要了
要设置为守护线程必须在线程开启之前,否则会抛出IllegalThreadStateException异常
怎么设置为守护线程?
Thread的deamon属性:true表示守护线程,false表示用户线程
Thread初始默认用户线程 即daemon=false
thread.setDaemon(false);//thread设置为用户线程(不设也是默认用户线程)
thread.start();//先设置再开启
thread.setDaemon(true);//thread设置为守护线程
thread.start();//先设置再开启
6.多线程
多线程就是一个程序可以实现多个任务 ,本质在于CPU是多内核的,理论上可以同时执行多个线程
1.并发和并行
并发:在一段时间内 多个线程依次做同一件事 就像卖票 线上线下都在卖,卖都都是那些票
并行:同一个时间 多个线程同时做多件事 各做个的
(并发卖票 并行高考 并发是关联的 并行互不干扰 你考得怎么样跟我无关)
2.多线程的优点:
提高程序的响应
提高cpu的利用率
改善程序结构,将复杂的任务分给多个线程,独立运行
3.线程同步(多线程存在的问题)
多个线程同时读一份资源时,可能引起冲突,就需要同步机制 就是排队加锁 实现先来后到
确保一个时间点只有一个线程访问共享资源。可以给共享资源加一把锁,哪个线程获取了这把锁,才有权利访问该共享资源。
我感觉同步锁就是一个专一的访问标记,谁有同步锁就只访问谁
加锁的问题:在多线程竞争下加锁,释放锁会导致比较多的上下文切换和调度延时,引起性能问题
3.1.synchronized锁
synchronized可以同步方法或代码块
锁对象:
这里的同步锁就是一个对象 同步锁对象 synchronized需要传入一个对象
1.普通同步方法:锁是当前实例对象
2.静态同步方法:锁是当前类的Class对象 只有一个
3.同步方法块:锁是括号内配置的对象
synchronized修饰非静态方法时,锁标志默认this 有几个对象就有几个锁
synchronized修饰静态方法时,锁标志是Class类的对象(每个类被加载到内存中时,都会为该类创建一个Class类的对象,用于封装类的信息,一个类即使有多个对象,Class类的对象只有一个)
3.2.Lock锁(常用)
ReentrantLock(独享锁,是一个类,有公平锁和非公平锁)实现了Lock接口
与synchronized相同的并发性和内存语义,在实现线程安全的控制中,比较常用,可以显示加锁,释放加锁(自带对象 synchronized还需要传一个进去 )
自带对象:同步锁使用Lock对象充当。
ReentrantLock lock=new ReentrantLock();//new一个锁对象
lock.lock();//加锁 内部维护了一个所标志
lock.unlock();//释放锁 最好写在finally里面 怎么都会释放锁
3.3.两种锁的比较
1.synchronized:
synchronized是关键字,(需要JVM实现)是隐式锁,自动添加,自动释放(执行完 或者出现异常会自动释放) 可以修饰代码块和方法
需要靠对象头中的标志记录锁的状态(即需要传进一个对象)
非公平锁(等待锁的时候不需要排队 谁抢到谁执行)
2.ReentrantLock:
ReentrantLock是类(需要lock(),unlck()方法配合try finally语句块使用),是显示锁 手动添加 手动释放
只能修饰代码块
内部维护锁的状态标志 不需要传对象
可以实现公平锁 内部维护了一个同步队列(ReentrantLock lock=new ReentrantLock(true)就是公平锁 没写就是默认非公平锁)
7.线程锁死
1.什么是线程锁死?
不同的线程分别占用对方需要的同步资源不放弃,都在等对方放弃,就形成了死锁 出现死锁后不会出现异常,不会出现提示,只是所有线程都处于阻塞状态,无法继续(我想要你手里的红中 你想要我手里的三条 你不想给我 我也不想给你 大家都别想赢---给我死)
8.线程通讯
1.什么是线程通讯?
线程通信:指的是多个线程通过相互牵制,相互调度,即线程间的相互作用。
类比就是 你出一张牌 然后再到我出一张牌 或者两个线程交替打印1~100 即把线程关联起来 相互影响作用
2.怎么控制线程 怎么让他们相互影响呢?
三个方法:
1.wait()当前线程进入堵塞状态,释放同步监视器
2.notify()唤醒被wait的一个线程 要是有多个线程 就唤醒优先级高的那个
3.notifyAll()唤醒所有被wait的线程
三个方法必须在同步方法或者同步代码块中,用同步锁对象调用该方法
3.案例:模拟生产者消费者
让生产者生成一个产品 然后消费者消费 没有产品了生产者又生产 然后消费者又消费 生产和消费是两个线程 即只是两个功能
柜台:
package feifan;
public class Counter {
int num=1;//产品
//生产者生产
public synchronized void add() throws InterruptedException {//加锁 非静态方法 对象是 this 只有一个
if (num==0){//没有产品-->生产者生产
num++;this.notify();//唤醒
System.out.println("生产者生产商品");
}else {
this.wait();//有商品的话 生产者等待 等消费者消费产品
}
}
//消费者消费
public synchronized void sub() throws InterruptedException {
if (num==0){//如果没有产品 消费者等待 等别人生产
this.wait();
}else {
num--;
System.out.println("消费者拿走商品");
this.notify();//有产品就唤醒消费
}
}
}
生产者线程:
package feifan;
public class Productor extends Thread {
//生产者线程
Counter c;//因为只有一个生产者 所以构造方法传进去
public Productor(Counter c) {
this.c = c;
}
@Override
public void run() {//线程具体功能在run()方法完成
while (true){
try {
c.add();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
消费者线程:
package feifan;
public class Customer extends Thread {
//消费者线程
Counter c;
public Customer(Counter c) {
this.c = c;
}
@Override
public void run() {
while (true){
try {
Thread.sleep(1000);
c.sub();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
测试:
package feifan;
public class PCTest {
public static void main(String[] args) {
Counter c=new Counter();//柜台 只有一个
Productor p=new Productor(c);
Customer customer=new Customer(c);
p.start();//开启生产者线程
customer.start();//开启消费者线程
}
}
结果: