Java基础到高级编程

#Section-1

1.windows下dos命令:
dir:列出当前目录下的文件以及文件夹
cd:进入指定目录
cd…退回到上一级目录
cls:清屏
md:创建目录
rd:删除目录
del:删除文件
notepad:创建文件
type:显示文本文件内容命令
ren:更改文件名命令
del:删除文件命令
tree:显示磁盘目录结构命令

2.java语言特点

优点:简单 面向对象 高性能 分布式处理 多线程 健壮性 动态 结构中立 安全性 开源 跨平台

缺点:效率低

c、c++ 属于编译性语言
Java Python Ruby 属于解释性语言

JRE(Java Run Environment):java运行环境
包含java虚拟机和java程序所形成的核心类库

JDK(Java Development Kit) :java开发工具包
包含java的开发工具:编译工具,打包工具
总结:使用JDK开发java程序交给JRE去运行
JDK安装路径下的目录:
1.bin:存放可执行程序:如编译器,运行工具,打包工具,文档生产工具

2.db:小型数据库

3.jie:java程序运行时环境

4.include:由于JDK是通过c和c++实现的,因在启动时需要引入一些c语言的头文件,该目录就是用于存放这些头文件的

5.lib:library  java类库或库文件,是开发工具使用的归档包文件

6.src.zip:src的压缩文件,存放jdk核心类的源代码

4.环境变量:
PATH:命令的搜索路径。在cmd终端运行dos命令时命令对应一个特定功能,特定功能由对应应用程序实现,PATH里面存放命令的搜索路径

CLASSPATH:class文件、jar包 的搜索路径。保存的是一些目录和jar文件的地址,这些路径是为java程序在编译和运行时搜索类而用的,为java程序所依赖的接口、类指定一个搜索路径。
	一般不需要配置 默认值为.当前目录
	动态提供类加载选项的路径  -cp

5.进制转换
二进制 0b|B开头
八进制 0 开头
十进制 常见整数
十六进制 0x|X开头

6.原反补码
原码:最高位为符号位 0为正 1为负

反码:正数的反码与其原码相同   负数的反码是对其原码逐位取反,符号位不变

补码:正数的补码与其原码相同   负数的补码是在其反码的末位加1 

**.计算机运算时都是以补码运算

7.数据类型:基本数据类型 引用数据类型
基本数据类型:4类8种
整数型:byte short int long
浮点型:float 4字节 double 8字节
字符型:char 2字节0~65535
布尔型

8.类型转换
隐式类型转换
将表示范围小的赋值给表示范围大的,类型自动转换为大的
显式类型转换
大类型赋值给小的,必须强制转换

#Section-2

1.字符和字符串参与运算
字符串 + 其他任意类型数据,得到的结果都是字符串

2.运算符
&运算时 结果的正负符号只和左操作数有关
三元运算符: 条件 ? 表达式1 : 表达式2
3. if() {

}else

4.循环
for
while
do while

##Section-3
1.代码优化:
\x 转义字符
\t 相当于一次tab键
\r 回车
\n 换行
" 输出”
’ 输出’
2.break:结束整个循环
continue:结束 本次循环
3.数组:
动态初始化:
数据类型[]数组名 = new 数据类型[]数组长度
静态初始化:
数据类型【】数组名 = new 数据类型【】{元素12314}
数据类型【】数组名 = {元素123456}

4.方法定义格式
修饰符 返回值类型 方法名(参数类型 参数名){}

方法重载:同一个类中,两个方法的名字相同,参数列表不同,构成重载

#Section-4
1.折半查找/二分查找

/**

  • 折半查找:从有序数组中查找指定元素值,返回对应下标

  • @Author: yfl

  • @Date: 2018/10/30/030 10:02

  • @Version: 1.0
    */
    public class Test_01 {
    public static void main(String[] args) {
    int[] arr = new int[]{1,3,5,7,9,11,12,15};
    // getIndex(9,arr);
    System.out.println("index: " + getIndex(9,arr));
    }
    public static int getIndex(int value,int[] arr) {
    //折半查找算法理解
    int si = 0;
    int ei = arr.length - 1;
    int mi = (si + ei) / 2;

     while (true) {
         if (arr[mi] ==value)
             return mi;
         else if(arr[mi] > value) {
             ei = mi - 1;
         }else {
             si = mi + 1;
         }
         mi = (si + ei) / 2;
         if (si > ei)
             break;
     }
    
     return -1;
    

    }

}

2.冒泡排序

public class Test_02 {
public static void main(String[] args) {
int[] arr = {9,5,7,4,6,2,87,95,15};
bubblesort(arr);
outArry(arr);
}

/**
 * 冒泡排序
 * @param arr
 */
public static void bubblesort(int[] arr) {
    for (int i = 1; i < arr.length;i++) {
        for (int j = 0 ;j < arr.length-i;j++) {
            if (arr[j] > arr[j+1]) {
                arr[j] = arr[j] ^ arr[j+1];
                arr[j+1] = arr[j] ^ arr[j+1];
                arr[j] = arr[j] ^ arr[j+1];
            }

        }
    }
}

/**
 * 遍历数组
 */
public static void outArry(int[] arr) {
    for (int i = 0;i < arr.length;i++) {
        System.out.println(arr[i]);
    }
}

}

3.二维数组:
格式:int[][] arr = new int[3][2]
数据类型 数组名[][] = new 数据类型[m][n]
遍历二维数组
先遍历第一维,然后遍历一维内的第二维

#Section-5

对象
创建对象的步骤
Student st = new Student(“zhangsan”,23)
1.Student.class加载到内存
2.main栈帧声明一个引用变量
3.在堆中开辟内存创建对象
4.给属性以默认初始值
5.属性进行显示的 初始化
6.构造方法进行比较,赋值给属性,然后弹栈
7.将堆中对象的地址给s
封装
好处:隐藏代码细节,提供公共访问方式,简化操作,提高安全性,提高代码复用性。
原则:把不需要对外提供的内容隐藏起来
把属性隐藏,提供公共方法对其访问

private 关键字

构造器

get,set方法

在类的成员函数内部,有一个this引用,指向调用方法的对象 

成员变量和局部变量的区别

构造方法

给成员变量赋值的两种方式
setName()方法
构造方法

static关键字
修饰成员方法
修饰数据成员
静态数据成员只初始化一次、【随着类加载而加载】
静态数据成员也是类成员,可以通过类名来访问
类外访问 public
类内访问 private

static修饰的方法
可以由类名调用
也可以由对象调用

static

方法内部,不能出现普通数据成员,本质上,内部没有this引用

#Section-6

随机数的产生
public static void main double radom():返回带正号的double值,范围:[0.0,1.0);

例如:产生一个1到100的随机数
int number = (int)(Math.radom()*100) + 1;

继承

  • A:继承的好处

    • a:提高了代码的复用性
    • b:提高了代码的维护性
    • c:让类与类之间产生了关系,是多态的前提
  • B:继承的弊端

    • 类的耦合性增强了。

    • 开发的原则:高内聚,低耦合。

    • 耦合:类与类的关系

    • 内聚:就是自己完成某件事情的能力

  • D:继承的注意事项

    • a:子类只能继承父类所有非私有的成员(成员方法和成员变量)
    • b:子类不能继承父类的构造方法,但是可以通过super(马上讲)关键字去访问父类构造方法。
    • c:不要为了部分功能而去继承
    • 项目经理 姓名 工号 工资 奖金
    • 程序员 姓名 工号 工资
  • E:this和super的区别和应用)(掌握)

    • A:this和super都代表什么【this.成员 super.成员】

      • this:代表当前对象的引用,谁来调用我,我就代表谁
      • super:代表当前对象父类部分的引用
    • B:this和super的使用区别

      • a:调用成员变量
        • this.成员变量 调用本类的成员变量,也可以调用父类的成员变量
        • super.成员变量 调用父类的成员变量
      • b:调用构造方法
        • this(…) 调用本类的构造方法
        • super(…) 调用父类的构造方法
      • c:调用成员方法
        • this.成员方法 调用本类的成员方法,也可以调用父类的方法
        • super.成员方法 调用父类的成员方法
    • C: this() super()必须在代码段的第一行; this() super() 有且只能有一个出现。

#Section-8

1.内部类:
1.成员内部类
2.静态内部类
3.局部内部类
4.匿名内部类
1.成员内部类:
1.定义在一个类内部成员的位置的类;
2.只能由外部类的对象来访问;
//成员数据只能由对象访问,静态数据类和对象都可以访问;
2.静态内部类
1.定义在一个类内部成员的位置,由static关键字修饰;
2.只能在内部类中直接访问外部类的静态成员;访问外部类非静态成员需要创建外部类对象;
3.局部内部类
1.定义在方法或者其他代码块中的类,跟局部变量同级别;

4.匿名内部类
1.该类没有名字
2.此种内部类的使用需要依托于接口或者抽象类而存在;
3.当接口中只有一个抽象方法时,该接口可以被叫做函数式借口;当匿名内部类实现函数式接口时,可以使用简便写法,即Lambda表达式。

2.包装器类
1.集合和数组类似,也是一种存储数据的容器,但是存储的是引用数据类型
2.将基本数据类型进行封装
byte Byte
short Short
int Integer
long Long

 float    Float
 double   Double

 char     Character
 boolean  Boolean

3.自动装箱和自动拆箱

3.Object类:是所有类的父类:超类;默认所有的类都隐式继承Object类;
0.toString:返回该对象的字符串表示。该方法返回一个字符串,它的值等于:
getClass().getName() + ‘@’ + Integer.toHexString(hashCode())

1.equals():
== :
在默认情况下,没有任何区别,都是用于判断两个对象的地址值是否相等;在一个类中,如果重写了equals方法,该方法就具有了自己的比较逻辑;
引用类型的比较,一般用equals方法,== 一般用于基本数据类型的比较;

4.String类:
0.final修饰的类
1.String类型的对象内容不可变;
String s = “123”
Stringbuffer
StringBuilder
2.equals方法
String s1 = “123”
String s2 = “123”

5.选择排序
从左往右按照下标,依次选择出来最小/最大的数值往下标处放。

排序 arr.length-1次,根据下标排序,minIndex最小值的下标;
for(int i = 0;i < arr.length-1;i++){
int minIdex = i;
for(int j = i+1;j <length;j++){
if(arr[j] < arr[minIndex])
minIndex = j;
}
//互换指定位置值与最小值
if(minIndex != i){
arr[minIndex] = arr[minIndex] ^ arr[i];
arr[i] = arr[minIndex] ^ arr[i];
arr[minIndex] = arr[minIndex] ^ arr[i];
}
}

6.插入排序
[11] 0 2 9 5 7 12
第一次 [0 11] 2 9 5 7 12
第二次 [0 2 11] 9 5 7 12
第三次 [0 2 9 11] 5 7 12
第四次 [0 2 5 9 11] 7 12
第五次 [0 2 5 7 9 11] 12
第六次 [0 2 5 7 9 11 12]

for(int i = 1,i<arr.length;i++){
	int num = arr[i];
	int j;
	for(j = i-1;j >= 0;j--){
		if(arr[j] > num){
			arr[j+1] = arr[j];	
			}
		}else
			break;
}
if(j+1 != i){
arr[j+1] = num;
}

#Section-9
集合:
Collection:单列集合
List:有序集合,元素可重复;
ArrayList:底层实现是数组,查找快;
LinkedList:底层实现是双向链表,插入和删除快;
Set:无序集合,元素不重复,判断元素是否是同一个元素的方法是通过调用元素对象的hashCode和equals方法;
HashSet:主要用于数据检索
TreeSet:主要用于排序

Map:双列集合
数据类型:K,V
K和Set集合中的元素一样,无序不可重复;

ArrayList:
1.无序元素可重复;
2.元素可以使null;
3.初始容量大小是10,增量是50%,在创建ArrayList对象时可以指定容量的大小;Vector初始容量是10,增量是100%;
4.ArrayList是线程不安全的,不同步的,但是效率高;Vector是线程安全的,同步的,但是效率低;

方法实现:
1.添加:add
2.遍历[获取]:
    1.通过get(int index)方法和for循环来遍历;
    2.增强for循环
    3.通过forEach方法遍历集合

Set:没有索引,没有特有方法
hashSet:
1.元素无序,元素不可重复
2.保证元素不可重复的机制:只要两个对象调用hashCode的值相等,调用equals方法的返回值为true,则表示这两个对象相等,此时,只能有一个添加到set集合中
3.removeAll和retainAll
4.该集合没有索引,遍历方式
1.使用Iterator对象做迭代遍历,使用方法和List集合一致
2.for(Object o:s){o}
3.s1.forEach((o)->{操作})

treeSet:
    1.默认内部是按照元素的字典顺序排序的:
    2.可以自定义排序规则
        1.实现Comparable接口,重写compare方法
        2.实现Comparator接口,相当于自定义了比较器,重写compareTo方法
    3.在TreeSet中添加的元素所属的类需要实现Comparable接口
    4.遍历方式:和Hashset一致
    5.注意:在TreeSet中,最好只添加同一种数据类型的元素:

    Comparable:
    Comparator:
        int comporeTo(T o):该方法用于比较调用该方法的对象和传递对象的大小;

Map集合:
1.双列集合:
2.形式是:Map<K,V>
3.Map集合的key和Set集合完全一致(不可重复,无序、key值可以为null、value任意)
4.Map集合的遍历方法
1.将map集合的所有key转化成set集合,可以通过key来找到value;
2.将map集合的所有K,V键值对转化成Entry对象,存储在Set集合中

##△△△△△△△△△△△△△△△==和wquals的区别△△△△△△△△△△△△△△△△△△△△△△△△△△△

对于基本数据类型来说,比的是值
对于引用数据类型来说,比的是两个对象的地址
equals:
只能用于引用数据类型,本质也是

对于没有重写equals()方法的类,比的是两个对象的地址值
对于重写了equals()方法的String类说,比的是两个对象的值

#Section-9

泛型:
1.不确定的类型
2.泛型的好处:
1.提高安全性【将运行期的错误转换到编译期】
2.省去强转的麻烦
3.
1.<>中放的必须是引用数据类型
2.前后的泛型必须一致,或者后面的泛型可以省略不写(1.7的新特性菱形泛型)
4.早期的Object类型可以接收任意的对象类型,但是在实际的使用中,会有类型转换的问题。也就存在这隐患,所以Java提供了泛型来解决这个安全问题。
5.
1.泛型类:public class 类名<泛型类型1,…>
2.泛型方法:public<泛型类型> 返回类型 方法名(泛型类型 变量名)
3.泛型接口:public interface 接口名<泛型类型>
6.泛型通配符:
1.向下限定:? extends E
2.向上限定:? super E

枚举:是指将变量一一列举出来,变量的值只限于列举出来的值的范围内;
1.关键字:enum
2.枚举元素,必须出现在第一行,中间用“,”隔开,最后一个后面加上“;”,也可以不加
3.枚举类型中可以包含 数据成员、成员函数、构造器,枚举类型中构造器必须私有
4.枚举元素必须私有

实现形式:
第一种:单利模式第三种形式,多写几个实例即可
    public class Week{
        public static final Week MON = new Week();
        public static final Week TUE = new Week();
        public static final Week WED = new Week();

        private Week(){}
    }

第二种:有参构造器实现
    public class Week{
        public static final Week MON = new Week("星期一");
        public static final Week TUE = new Week("星期二");
        public static final Week WED = new Week("星期三");

        private String name;
        private Week(String name){this.name = name;}
        public String getName(){return name;}

    }

第三种:形式2基础上定义抽象方法show();
    punlic abstract class Week{
        public static final Week MON = new Week("星期一"){
            @override
            public void show(){syso("星期一")}
        }
    ...
    ...
    public abstract void show();

    }

	  定义枚举类要用关键字enum
	  所有枚举类都是Enum的子类
	  枚举类的第一行上必须是枚举项,最后一个枚举项后的分号是可以省略的,但是如果枚举类有其他的东西,这个分号就不能省略,建议不要省略。
	  枚举类可以有构造器,但必须是private的,它默认的也是private的。
	  枚举类也可以有抽象方法,但是枚举项必须重写该方法。
	  枚举在switch语句中的使用,在switch中,枚举项可以独立存在,不依赖 枚举类型。

枚举类的常见方法

	  int ordinal()  获取枚举元素编号,从0开始,依次递增
	  int compareTo(E o)	比较枚举元素编号
	  String name()
	  String toString()
	  <T> T valueOf(Class<T> type,String name) 通过字节码文件,获取枚举项valueOf(Week.class,"MON");
	  values()  获取到枚举类所有取值
	  此方法虽然在JDK文档中查找不到,但每个枚举类都具有该方法,它遍历枚举类的所有枚举值非常方便

#Section-10

GUI:
一.容器
1.Frame:常见的窗口,他是Windows类的子类
1.Frame对象有标题,允许通过拖拉来改变窗口的位置、大小
2.初始化时为不可见,可用setVisible(true)来显示
3.默认使用BorderLayout作为其布局管理器

2.Panel:AWT中另一个典型的容器,外在表现为一个矩形区域,该区域内可盛装其他组件
1.可作为容器来盛装其他组件,为放置其他组件提供空间
2.不能单独存在,必须放置到其他容器中
3.默认使用FlowLayout作为其布局管理器

3.ScrollPane:带滚动条的容器,不能独立存在,必须被添加到其他容器中
1.可作为容器来盛装其他组件,当组件占用空间过大时,ScrollPane会自动产生滚动条,当然,也可以通过指定特定的构造器参数来指定默认具有滚动条
2.不能单独存在,必须放在其他容器中
3.默认使用BorderLayout作为其布局管理器。由于ScrollPane通常用于盛装其他容器,所以通常不允许改变它的布局管理器

二.布局管理器
1.Flowlayout;组件像水流一样向某个方向流动(排列),遇到障碍(边界)就折回,重头开始排列,默认从左向右排列,遇到障碍就从下一行开始
2.BorderLayout
3.GridLayout
4.GridBagLayout
5.CardLayout

三:基本组件
1.Button:按钮,可接受单击操作
2.Canvas:用于画图的绘布
3.Checkbox:复选框组件(也可变成单选框组件)
4.Choice:下拉式选择框组件
5.Frame:窗口,在GUI里通过该类创建窗口
6.Lable:标签,用于放置提示性文本
7.List:列表框组件,用于添加多项条目
8.Panel:不能单独存在的基本容器类,必须放到其他容器中
9.Scrollbar:滚动条组件,当创建一个滑动条时,必须指定它的方向、初始值、滑块的大小,最小值和最大值
10.ScrollPane:带水平及垂直滚动条的容器组件
11.TextArea:多行文本域
12.TextField:单行文本框

四:对话框
Dialog

五:事件处理
1.JAVA事件模型的流程
AWT的事件处理机制是一种委派式的事件处理方式–普通组件(事件源)将事件的处理工作委托给特定的对象(事件监听器);
当该事件源发生指定的事件时,就通知所委托的事件监听器,由事件监听器来处理这个事件。
2.事件和事件监听器
1.低级事件:基于特定动作的事件
2.高级事件:基于语义的事件
3.事件适配器
事件适配器是监听接口的空实现–事件适配器实现了监听接口,并为该接口里的每个方法都提供了实现,这种实现是一种空实现
及方法体内没有任何代码的实现;
4.使用内部类实现事件监听器
5.使用外部类实现事件监听器
6.类本身作为事件监听器类
7.匿名内部类实现监听器

六:AWT菜单
1.菜单条、菜单和菜单项
2.右键菜单

七:在AWT中绘图

八:处理位图

九:剪贴板

十:拖放功能

#Section-11
异常:异常就是Java程序在运行过程中出现的错误。

1.异常的分类
通过API查看Throwable
Error
服务器宕机,数据库崩溃等,比较严重的,无法控制的。
Exception
异常 只要是程序运行没有得到正常的结果,都属于异常。
int a = 10; int b = sc.nextInt(); int c = a / b; syso©;
int[] array = new int[10000];//栈空间内存不够40000字节
2.异常的继承体系
Throwable
Error
Exception
RuntimeException
3.JVM默认是如何处理异常的
main函数收到这个问题时,有两种处理方式:
a:自己将该问题处理,然后继续运行
b:自己没有针对的处理方式,只有交给调用main的jvm来处理
jvm有一个默认的异常处理机制,就将该异常进行处理.
并将该异常的名称,异常的信息.异常出现的位置打印在了控制台上,同时将程序停止运行
4.异常处理的两种方式
a:try…catch…finally
try catch
try catch finally
try finally
基本格式: try{
可能出现问题的代码段【throw语句】;
}catch(异常类型1 引用名){
问题的解决方案;
}catch(异常类型n 引用名){
问题的解决方案;
}finally{
额外释放资源;
}
b:throws

5.编译期异常和运行期异常的区别
Java中的异常被分为两大类:编译时异常和运行时异常。
所有的RuntimeException类及其子类的实例被称为运行时异常,其他的异常就是编译时异常

编译时异常
	Java程序必须显示处理[try-catch,thorws],否则程序就会发生错误,无法通过编译
	流相关的异常,就属于编译时异常
运行时异常
	无需显示处理[try-catch,throws],也可以和编译时异常一样处理
	程序员所犯的错误,需要回来修改代码【越界、空指针】

#Section-12 线程
1.什么是线程
线程是程序执行的一条路径, 一个进程中可以包含多条线程
QQ聊天 进程 你在给同学发信息的同时,能否接收其他同学的消息?提高效率
进程,是一个应用程序的执行流程,进程是资源分配的最小单位
多线程并发执行可以提高程序的效率, 可以同时完成多项工作
2.多线程的应用场景
红蜘蛛同时共享屏幕给多个电脑
迅雷开启多条线程一起下载
QQ同时和多个人一起视频
服务器同时处理多个客户端请求

多线程并行和并发的区别
并行就是两个任务同时运行,就是甲任务进行的同时,乙任务也在进行。(需要多核CPU)
并发是指两个任务都请求运行,而处理器只能按受一个任务,就把这两个任务安排轮流进行,由于时间间隔较短,使人感觉两个任务都在运行。
比如我跟两个网友聊天,左手操作一个电脑跟甲聊,同时右手用另一台电脑跟乙聊天,这就叫并行。
如果用一台电脑我先给甲发个消息,然后立刻再给乙发消息,然后再跟甲聊,再跟乙聊。这就叫并发。

多线程程序实现的方式1
1.继承Thread
定义类继承Thread
重写run方法
把新线程要做的事写在run方法中
创建线程对象
开启新线程, 内部会自动执行run方法

		public class Demo2_Thread {
	
			/**
			 * @param args
			 */
			public static void main(String[] args) {
				MyThread mt = new MyThread();							//4,创建自定义类的对象
				mt.start();												//5,开启线程
				
				for(int i = 0; i < 3000; i++) {
					System.out.println("bb");
				}
			}
		
		}
		class MyThread extends Thread {									//1,定义类继承Thread
			public void run() {											//2,重写run方法
				for(int i = 0; i < 3000; i++) {							//3,将要执行的代码,写在run方法中
					System.out.println("aaaaaaaaaaaaaaaaaaaaaaaaaaaa");
				}
			}
		}

多线程程序实现的方式2
2.实现Runnable
定义类实现Runnable接口
实现run方法
把新线程要做的事写在run方法中
创建自定义的Runnable的子类对象
创建Thread对象, 传入Runnable
调用start()开启新线程, 内部会自动调用Runnable的run()方法

		public class Demo3_Runnable {
			/**
			 * @param args
			 */
			public static void main(String[] args) {
				MyRunnable mr = new MyRunnable();						//4,创建自定义类对象
				//Runnable target = new MyRunnable();
				Thread t = new Thread(mr);								//5,将其当作参数传递给Thread的构造函数
				t.start();												//6,开启线程
				
				for(int i = 0; i < 3000; i++) {
					System.out.println("bb");
				}
			}
		}
		
		class MyRunnable implements Runnable {							//1,自定义类实现Runnable接口
			@Override
			public void run() {											//2,重写run方法
				for(int i = 0; i < 3000; i++) {							//3,将要执行的代码,写在run方法中
					System.out.println("aaaaaaaaaaaaaaaaaaaaaaaaaaaa");
				}
			}
			
		}

两种方式的区别
查看源码的区别:
a.继承Thread: 由于子类重写了Thread类的run(), 当调用start()时, 直接找子类的run()方法
b.实现Runnable: 构造函数中传入了Runnable的引用, 成员变量记住了它, start()调用run()方法时内部判断成员变量Runnable的引用是否为空, 不为空编译时看的是Runnable的run(),运行时执行的是子类的run()方法

继承Thread
好处是:可以直接使用Thread类中的方法,代码简单
弊端是:如果已经有了父类,就不能用这种方法
实现Runnable接口
好处是:即使自己定义的线程类有了父类也没关系,因为有了父类也可以实现接口,而且接口是可以多实现的
弊端是:不能直接使用Thread中的方法需要先获取到线程对象后,才能得到Thread的方法,代码复杂

匿名内部类实现线程的两种方式
继承Thread类

	new Thread() {													//1,new 类(){}继承这个类
		public void run() {											//2,重写run方法
			for(int i = 0; i < 3000; i++) {							//3,将要执行的代码,写在run方法中
				System.out.println("aaaaaaaaaaaaaaaaaaaaaaaaaaaa");
			}
		}
	}.start();

实现Runnable接口

	new Thread(new Runnable(){										//1,new 接口(){}实现这个接口
		public void run() {											//2,重写run方法
			for(int i = 0; i < 3000; i++) {							//3,将要执行的代码,写在run方法中
				System.out.println("bb");
			}
		}
	}).start(); 

休眠线程
Thread.sleep(毫秒,纳秒), 控制当前线程休眠若干毫秒1秒= 1000毫秒 1秒 = 1000 * 1000 * 1000纳秒 1000000000
守护线程
setDaemon(boolean), 设置一个线程为守护线程, 该线程不会单独执行, 当其他非守护线程都执行结束后, 自动退出
加入线程
join(), 当前线程暂停, 等待指定的线程执行结束后, 当前线程再继续
join(int), 可以等待指定的毫秒之后继续

礼让线程
yield让出cpu
建议性的
设置线程的优先级
setPriority(int)设置线程的优先级,参数越大优先级越高。
建议性的
多线程(同步代码块)
1.什么情况下需要同步
当多线程并发, 有多段代码同时执行时, 我们希望某一段代码执行的过程中CPU不要切换到其他线程工作. 这时就需要同步.
如果两段代码是同步的, 那么同一时间只能执行一段, 在一段代码没执行结束之前, 不会执行另外一段代码.
2.同步代码块
使用synchronized关键字加上一个锁对象来定义一段代码, 这就叫同步代码块
多个同步代码块如果使用相同的锁对象, 那么他们就是同步的

多线程(同步方法)
使用synchronized关键字修饰一个方法, 该方法中所有的代码都是同步的

线程安全问题
多线程并发操作同一数据时, 就有可能出现线程安全问题
使用同步技术可以解决这种问题, 把操作数据的代码进行同步, 不要多个线程一起操作

		public class Demo2_Synchronized {

			/**
			 * @param args
			 * 需求:铁路售票,一共100张,通过四个窗口卖完.
			 */
			public static void main(String[] args) {
				TicketsSeller t1 = new TicketsSeller();
				TicketsSeller t2 = new TicketsSeller();
				TicketsSeller t3 = new TicketsSeller();
				TicketsSeller t4 = new TicketsSeller();
				
				t1.setName("窗口1");
				t2.setName("窗口2");
				t3.setName("窗口3");
				t4.setName("窗口4");
				t1.start();
				t2.start();
				t3.start();
				t4.start();
			}
		
		}
		
		class TicketsSeller extends Thread {
			private static int tickets = 100;
			static Object obj = new Object();
			public TicketsSeller() {
				super();
				
			}
			public TicketsSeller(String name) {
				super(name);
			}
			public void run() {
				while(true) {
					synchronized(obj) {
						if(tickets <= 0) 
							break;
						try {
							Thread.sleep(10);//线程1睡,线程2睡,线程3睡,线程4睡
						} catch (InterruptedException e) {
							e.printStackTrace();
						}
						System.out.println(getName() + "...这是第" + tickets-- + "号票");
					}
				}
			}
		}

死锁
多线程同步的时候, 如果同步代码嵌套, 使用相同锁, 就有可能出现死锁
尽量不要嵌套使用

两个线程间的通信
1.什么时候需要通信
多个线程并发执行时, 在默认情况下CPU是随机切换线程的
如果我们希望他们有规律的执行, 就可以使用通信, 例如每个线程执行一次打印
2.怎么通信
如果希望线程等待, 就调用wait()
如果希望唤醒等待的线程, 就调用notify();
这两个方法必须在同步代码中执行, 并且使用同步锁对象来调用

三个或三个以上间的线程通信
多个线程通信的问题
notify()方法是随机唤醒一个线程
notifyAll()方法是唤醒所有线程
DK5之前无法唤醒指定的一个线程
如果多个线程之间通信, 需要使用notifyAll()通知所有线程, 用while来反复判断条件
在同步代码块中,使用哪个对象锁,就使用该对象调用wait方法
为什么wait方法和notify方法定义在Object类中?
因为锁对象可以是任意对象
sleep方法和wait方法有什么区别?
sleep方法必须传入参数值,即一个时间值,时间到了自动醒来
wait方法中参数可有可无,有参表示过了参数时间后进入等待,否则直接等待
sleep方法在同步函数或同步代码块中,不释放锁
wait方法在同步函数或同步代码块中,释放锁

互斥锁
1.同步
使用ReentrantLock类的lock()和unlock()方法进行同步
类似synchronized
2.通信
使用ReentrantLock类的newCondition()方法可以获取Condition对象
需要等待的时候使用Condition的await()方法, 唤醒的时候用signal()方法
不同的线程使用不同的Condition, 这样就能区分唤醒的时候找哪个线程了

线程组的概述和使用
A:线程组概述
Java中使用ThreadGroup来表示线程组,它可以对一批线程进行分类管理,Java允许程序直接对线程组进行控制。
默认情况下,所有的线程都属于【主线程组】
public final ThreadGroup getThreadGroup()//通过线程对象获取他所属于的组
public final String getName()//通过线程组对象获取他组的名字
我们也可以给线程设置分组
1,ThreadGroup(String name) 创建线程组对象并给其赋值名字
2,创建线程对象
3,Thread(ThreadGroup?group, Runnable?target, String?name)
4,设置整组的优先级或者守护线程

线程的五种状态:新建,就绪,运行,阻塞,死亡

线程池的概述和使用
A:线程池概述
程序启动一个新线程成本是比较高的,因为它涉及到要与操作系统进行交互。而使用线程池可以很好的提高性能,尤其是当程序中要创建大量生存期很短的线程时,更应该考虑使用线程池。线程池里的每一个线程代码结束后,并不会死亡,而是再次回到线程池中成为空闲状态,等待下一个对象来使用。在JDK5之前,我们必须手动实现自己的线程池,从JDK5开始,Java内置支持线程池
B:内置线程池的使用概述
DK5新增了一个Executors工厂类来产生线程池,有如下几个方法
public static ExecutorService newFixedThreadPool(int nThreads)
public static ExecutorService newSingleThreadExecutor()
这些方法的返回值是ExecutorService对象,该对象表示一个线程池,可以执行Runnable对象或者Callable对象代表的线程。它提供了如下方法
Future<?> submit(Runnable task)
Future submit(Callable task)
使用步骤:
创建线程池对象
创建Runnable实例
提交Runnable实例
关闭线程池

多线程程序实现的方式3
提交的是Callable

	// 创建线程池对象
	ExecutorService pool = Executors.newFixedThreadPool(2);

	// 可以执行Runnable对象或者Callable对象代表的线程
	Future<Integer> f1 = pool.submit(new MyCallable(100));
	Future<Integer> f2 = pool.submit(new MyCallable(200));

	// V get()
	Integer i1 = f1.get();
	Integer i2 = f2.get();

	System.out.println(i1);
	System.out.println(i2);

	// 结束
	pool.shutdown();

	public class MyCallable implements Callable<Integer> {

		private int number;
	
		public MyCallable(int number) {
			this.number = number;
		}
	
		@Override
		public Integer call() throws Exception {
			int sum = 0;
			for (int x = 1; x <= number; x++) {
				sum += x;
			}
			return sum;
		}
	
	}

#Section-12 IO流

1.概念
IO流用来处理设备之间的数据传输
Java对数据的操作是通过流的方式
Java用于操作流的类都在IO包中
流按流向分为两种:输入流,输出流。
流按操作类型分为两种:
字节流 : 字节流可以操作任何数据,因为在计算机中任何数据都是以字节的形式存储的
字符流 : 字符流只能操作纯字符数据,比较方便。
2.IO流常用父类
字节流的抽象父类:
InputStream
OutputStream
字符流的抽象父类:
Reader
Writer
3.IO程序书写
使用前,导入IO包中的类
使用时,进行IO异常处理
使用后,释放资源

FileInputStream
read()一次读取一个字节
read()方法返回值为什么是int
read()方法读取的是一个字节,为什么返回是int,而不是byte

	因为字节输入流可以操作任意类型的文件,比如图片音频等,这些文件底层都是以二进制形式的存储的,如果每次读取都返回byte,有可能在读到中间的时候遇到11111111
	那么这11111111是byte类型的-1,我们的程序是遇到-1就会停止不读了,后面的数据就读不到了,所以在读取的时候用int类型接收,如果11111111会在其前面补上
	24个0凑足4个字节,那么byte类型的-1就变成int类型的255了这样可以保证整个数据读完,而结束标记的-1就是int类型

FileOutputStream
write()一次写出一个字节

拷贝图片
FileInputStream读取
FileOutputStream写出

拷贝音频文件画原理图
案例演示
字节流一次读写一个字节复制音频mp3文件
弊端:效率太低

字节数组拷贝之available()方法
案例演示
int read(byte[] b):一次读取一个字节数组
write(byte[] b):一次写出一个字节数组
available()获取读的文件所有的字节个数
弊端:有可能会内存溢出

定义小数组
write(byte[] b)
write(byte[] b, int off, int len)写出有效的字节个数

BufferedInputStream和BufferOutputStream拷贝
A:缓冲思想
字节流一次读写一个数组的速度明显比一次读写一个字节的速度快很多,
这是加入了数组这样的缓冲区效果,java本身在设计的时候,
也考虑到了这样的设计思想(装饰设计模式后面讲解),所以提供了字节缓冲区流
B.BufferedInputStream
BufferedInputStream内置了一个缓冲区(数组)
从BufferedInputStream中读取一个字节时
BufferedInputStream会一次性从文件中读取8192个, 存在缓冲区中, 返回给程序一个
程序再次读取时, 就不用找文件了, 直接从缓冲区中获取
直到缓冲区中所有的都被使用过, 才重新从文件中读取8192个
C.BufferedOutputStream
BufferedOutputStream也内置了一个缓冲区(数组)
程序向流中写出字节时, 不会直接写到文件, 先写到缓冲区中
直到缓冲区写满, BufferedOutputStream才会把缓冲区中的数据一次性写到文件里

flush()方法
用来刷新缓冲区的,刷新后可以再次写出
close()方法
用来关闭流释放资源的的,如果是带缓冲区的流对象的close()方法,不但会关闭流,还会再关闭流之前刷新缓冲区,关闭后不能再写出

字节流读写中文
字节流读取中文的问题
字节流在读中文的时候有可能会读到半个中文,造成乱码
字节流写出中文的问题
字节流直接操作的字节,所以写出中文必须将字符串转换成字节数组
出回车换行 write("\r\n".getBytes());

字符流FileReader)
1.字符流是什么
字符流是可以直接读写字符的IO流
字符流读取字符, 就要先读取到字节数据, 然后转为字符. 如果要写出字符, 需要把字符转为字节再写出.
2.FileReader
FileReader类的read()方法可以按照字符大小读取

字符流FileWriter
FileWriter类的write()方法可以自动把字符转为字节写出
注意: Writer中自带缓冲区,注意刷新或关闭流。

什么情况下使用字符流
字符流也可以拷贝文本文件, 但不推荐使用. 因为读取时会把字节转为字符, 写出时还要把字符转回字节.
程序需要读取一段文本, 或者需要写出一段文本的时候可以使用字符流。
读取的时候是按照字符的大小读取的,不会出现半个中文
写出的时候可以直接将字符串写出,不用转换为字节数组

字符流是否可以拷贝非纯文本的文件
不可以拷贝非纯文本的文件
因为在读的时候会将字节转换为字符,在转换过程中,可能找不到对应的字符,就会用?代替,写出的时候会将字符转换成字节写出去
如果是?,直接写出,这样写出之后的文件就乱了,看不了了

带缓冲的字符流)
BufferedReader的read()方法读取字符时会一次读取若干字符到缓冲区, 然后逐个返回给程序, 降低读取文件的次数, 提高效率
BufferedWriter的write()方法写出字符时会先写到缓冲区, 缓冲区写满时才会写到文件, 降低写文件的次数, 提高效率

readLine()和newLine()方法
BufferedReader的readLine()方法可以读取一行字符(不包含换行符号)
BufferedWriter的newLine()可以输出一个跨平台的换行符号"\r\n"

LineNumberReader
LineNumberReader是BufferedReader的子类, 具有相同的功能, 并且可以统计行号
调用getLineNumber()方法可以获取当前行号
调用setLineNumber()方法可以设置当前行号

使用指定的码表读写字符)
FileReader是使用默o认码表读取文件, 如果需要使用指定码表读取, 那么可以使用InputStreamReader(字节流,编码表)
FileWriter是使用默认码表写出文件, 如果需要使用指定码表写出, 那么可以使用OutputStreamWriter(字节流,编码表)

序列流
1.什么是序列流
序列流可以把多个字节输入流整合成一个, 从序列流中读取数据时, 将从被整合的第一个流开始读, 读完一个之后继续读第二个, 以此类推.
2.使用方式
整合两个: SequenceInputStream(InputStream, InputStream)

内存输出流
1.什么是内存输出流
该输出流可以向内存中写数据, 把内存当作一个缓冲区, 写出之后可以一次性获取出所有数据
2.使用方式
创建对象: new ByteArrayOutputStream()
写出数据: write(int), write(byte[])
获取数据: toByteArray()

打印流的概述和特点
1.什么是打印流
该流可以很方便的将对象的toString()结果输出, 并且自动加上换行, 而且可以使用自动刷出的模式
System.out就是一个PrintStream, 其默认向控制台输出信息

2.使用方式
打印: print(), println()
自动刷出: PrintWriter(OutputStream out, boolean autoFlush, String encoding) ,只针对println方法,没什么大用处。
打印流只操作【数据目的】

标准输入输出流概述和输出语句
1.什么是标准输入输出流(掌握)
System.in是InputStream, 标准输入流, 默认可以从键盘输入读取字节数据
System.out是PrintStream, 标准输出流, 默认可以向Console中输出字符和字节数据
2.修改标准输入输出流(了解)
修改输入流: System.setIn(InputStream)
修改输出流: System.setOut(PrintStream)

数据输入输出流
1.什么是数据输入输出流
DataInputStream, DataOutputStream可以按照基本数据类型大小读写数据
例如按Long大小写出一个数字, 写出时该数据占8字节. 读取的时候也可以按照Long类型读取, 一次读取8个字节.
2.使用方式
DataOutputStream(OutputStream), writeInt(), writeLong()

#Section-13 网络
网络编程三要素之IP概述
每个设备在网络中的唯一标识
每台网络终端在网络中都有一个独立的地址,我们在网络中传输数据就是使用这个地址。
ipconfig:查看本机IP 192.168.12.42
ping:测试连接192.168.40.62
本地回路地址:127.0.0.1 255.255.255.255是广播地址
IPv4:4个字节组成,4个0-255。大概42亿,30亿都在北美,亚洲4亿。2011年初已经用尽。
IPv6:8组,每组4个16进制数。
1a2b:0000:aaaa:0000:0000:0000:aabb:1f2f
1a2b::aaaa:0000:0000:0000:aabb:1f2f
1a2b:0000:aaaa::aabb:1f2f
1a2b:0000:aaaa::0000:aabb:1f2f
1a2b:0000:aaaa:0000::aabb:1f2f

网络编程三要素之端口号概述
每个程序在设备上的唯一标识
每个网络程序都需要绑定一个端口号,传输数据的时候除了确定发到哪台机器上,还要明确发到哪个程序。
端口号范围从0-65535
编写网络应用就需要绑定一个端口号,尽量使用1024以上的,1024以下的基本上都被系统程序占用了。
常用端口
mysql: 3306
oracle: 1521
web: 80开头
tomcat: 8080
QQ: 4000
feiQ: 2425

网络编程三要素协议
UDP/IP TCP/IP 协议族(一大堆协议组成了一个组 STMP HTTP)
为计算机网络中进行数据交换而建立的规则、标准或约定的集合。
UDP
面向无连接,数据不安全,速度快。不区分客户端与服务端。 发送的是数据包。
TCP
   面向连接(三次握手),数据安全,速度略低。分为客户端和服务端。
三次握手: 客户端先向服务端发起请求, 服务端响应请求, 传输数据

Socket通信原理
A:Socket套接字概述:
网络上具有唯一标识的IP地址和端口号组合在一起才能构成唯一能识别的标识符套接字。
通信的两端都有Socket。
网络通信其实就是Socket间的通信。
数据在两个Socket间通过IO流传输。
Socket在应用程序中创建,通过一种绑定机制与驱动程序建立关系,告诉自己所对应的IP和port。

TCP协议
1.服务端
创建ServerSocket(需要指定端口号)
调用ServerSocket的accept()方法接收一个客户端请求,得到一个Socket
调用Socket的getInputStream()和getOutputStream()方法获取和客户端相连的IO流
输入流可以读取客户端输出流写出的数据
输出流可以写出数据到客户端的输入流
操作完成,关闭资源
2.客户端
创建Socket连接服务端(指定ip地址,端口号)通过ip地址找对应的服务器
调用Socket的getInputStream()和getOutputStream()方法获取和服务端相连的IO流
输入流可以读取服务端输出流写出的数据
输出流可以写出数据到服务端的输入流
操作完成,关闭资源

TCP协议代码优化)
流进行了优化 InputStream BufferedReader
OutputStream PrintStream
客户端

	Socket socket = new Socket("127.0.0.1", 9999);		//创建Socket指定ip地址和端口号
	InputStream is = socket.getInputStream();			//获取输入流
	OutputStream os = socket.getOutputStream();			//获取输出流
	BufferedReader br = new BufferedReader(new InputStreamReader(is));
	PrintStream ps = new PrintStream(os);
	
	System.out.println(br.readLine());
	ps.println("我想报名就业班");
	System.out.println(br.readLine());
	ps.println("爷不学了");
	socket.close();
  • 服务端

      ServerSocket server = new ServerSocket(9999);	//创建服务器
      Socket socket = server.accept();				//接受客户端的请求
      InputStream is = socket.getInputStream();		//获取输入流
      OutputStream os = socket.getOutputStream();		//获取输出流
      
      BufferedReader br = new BufferedReader(new InputStreamReader(is));
      PrintStream ps = new PrintStream(os);
      
      ps.println("欢迎咨询杰普软件");
      System.out.println(br.readLine());
      ps.println("报满了,请报下一期吧");
      System.out.println(br.readLine());
      server.close();
      socket.close();
    

UDP传输
UDP节点,不需要建立网络连接, 建立一个节点,准备数据包,直接发出去|直接收取,处理数据
1.发送Send
创建DatagramSocket, 随机端口号
创建DatagramPacket, 指定数据, 长度, 地址, 端口
使用DatagramSocket发送DatagramPacket
关闭DatagramSocket
2.接收Receive
创建DatagramSocket, 指定端口号
创建DatagramPacket, 指定数组, 长度
使用DatagramSocket接收DatagramPacket
关闭DatagramSocket
从DatagramPacket中获取数据
3.接收方获取ip和端口号
String ip = packet.getAddress().getHostAddress();
int port = packet.getPort();

	发送端
	String str = "what are you 干啥?";
	//整个套接字
	DatagramSocket socket = new DatagramSocket();
	//弄个数据包
	DatagramPacket packet = new DatagramPacket(
			str.getBytes(),str.getBytes().length,
			InetAddress.getByName("127.0.0.1"),6666);
	//发送数据包
	socket.send(packet);
	//关闭套接字
	socket.close();

	接收端
	//准备套接字   指定端口 
	DatagramSocket socket = new DatagramSocket(6666);
	//准备数据包  
	DatagramPacket packet = new DatagramPacket(new byte[1024], 1024);
	//接收数据
	socket.receive(packet);
	
	//获取数据
	byte[] arr = packet.getData();
	//获取有效的字节个数
	int len = packet.getLength();
	
	System.out.println(new String(arr,0,len));
	
	//关闭资源
	socket.close();
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值