Java进阶部分
进程部分:
进程中至少有一个线程负责执行java程序,而且这个线程运行的代码存在main方法中。正在运行的线程成为主线程。
扩展:jvm启动的不仅仅一个线程,各个线程在不断的切换中,只是切换的速度比较快而已,java中还有专门负责回收java线程的机制。
如何在自己的代码中自定义一个线程:
1、我们通过对api的查找,java已经提供对java线程处理的类,如Thread类。(Thread类用于描述线程,该类定义了一个功能,用于存储线程运行的代码,该存储功能就是run方法。也就是说run方法用于存储线程要执行的代码——复写run方法的原因)
第一种创建线程的方法:继承Thread类。
创建的步骤:
①:定义类继承Thread类
②:复写Thread类中的run方法,目的是将自定义运行代码存储在run方法中。 ③:调用Thraad的start方法(该方法有两个作用:启动线程;调用run方法)。
在多线程中每一次运行的结果都不一样:因为多个线程都在获取cpu的执行权,cup执行到谁谁就执行,明确一点,某一个时刻,只有一个线程在执行(多核除外),cup作者快速的切换,以达到看上去是一起执行的效果,我们可以形象的把多线程的执行方式是抢夺cup的资源。
多线程的特征:随机性——随抢到cup的资源哪个现正执行,创建两个线程,和主线程交替运行.他们之间的交替是随机的。
线程都有自己的默认的名称:Thread编号,该编号从0开始。
static Thread currentThread();用于获取当前线程;
getThread();用于获取线程名称;
设置线程的名称:setName()或者构造函数。
线程的集中状态:start() sleep() wait()notify() stop()
运行状态:正在运行的状态。
冻结状态:放弃了执行权,即使被唤醒了还是回到阻塞状态。
阻塞状态又叫临时状态:等待cup的执行权,也就是具备执行能力,但是没有执行权。
第二种创建线程的方法:Runnable。
步骤:
1、定义类实现Runnable接口。
2、覆盖Runnable中的run方法,将线程要运行的代码存储在run()方法中。
3、通过Thread类创建线程对象。
4、将Runable接口的子类对象作为实际参数传递给Thread类的构造方法。
5、调用Thread类的start()方法开启线程并调用Runnable接口子类的run()方法。
为什么要将Runnable的子类对象传递给Thread的构造函数呢?
因为自定义的run()方法所属的对象是Runnable接口的子类对象,所以让线程去执行指定
对象的run()方法,就必须明确该run()方法所属的对象。
Runnable实现方式的好处就是:避免单继承的局限性实现了功能的扩展。
继承Thread类:线程代码存放在Thread子类的run()中。
实现Runnable:线程的代码存在接口的子类的run()方法中。
多线程容易出现的安全问题:
问题的原因:当多条语句在操作同一个线程对多条语句只执行了一部分还没有执行完,另一个线程参与进来执行,导致共享数据的错误。
解决办法:对多条操作共享数据的语句,只能让一个线程都执行完,在执行的过程中,其他线程不可以参与执行。
java对于多线程的安全问题提供了的解决方式:代码块同步。
synchronized(object){需要被同步的代码块}。object如同锁,持有锁的代码块可以在同步中执行,没有锁的代码即使获得cpu的执行权,也进不了,也就是不能执行。
同步的另一种方式:将synchronized作为函数的修饰符——同步函数。
同步函数用的是哪一个锁呢?
函数需要被对象调用,那么函数都有一个所属的对象引用,就是this,所以同步函数的锁就是this。
如果函数被Static修饰之后,函数的锁已经不再是this,那么函数使用的锁是什么?
静态进内存时,内存中没有本类对象,但是一定有该类对应的字节码文件对象,类名.class,该对象的类型是Class。静态的同步方法,使用的锁是该方法所在类的字节码文件对象。
单例设计模式。(饿汉式、懒汉式)(部分知识详见java面向对象基础部分)
懒汉式特点:延时加载——多线程访问时存在安全问题,可以用他同步来解决,使用的锁为类所属的字节码对象。
死锁:同步中嵌套含有同步,就是拥有锁的线程相互不给锁。
创建线程需要解决的问题:
1、明确哪些是代码是多线程代码。
2、明确共享数据。
3、明确多线程中哪些语句是操作共享数据的。
集合框架:
为什么需要这么多的容器:每个容器对数据的存储方式是不同的,存储的方式就是数据的类型(数据的类型决定了数据的存储方式和操作方式)。集合内存储的对象的类型可以不一样而且存储的数目可以为任意值。
对容器就操作:增、删、改、查。
创建一个集合容器——使用Collection根接口的子类。
1、add()方法的参数类型是object。以便用于接收任意类型对象。
2、addAll()添加一堆的元素。
3、remove()方法;删除部分集合元素。
4、removeAll()方法;用于处理多个元素。
4、clear()方法;清空集合。
5、contains()方法;如果此 collection 包含指定的元素,则返回 true。
6、isEmpty()方法;判断集合是否为空相同的对象。
7、size()方法;获取集合元素的个数。
8、retainAll();取交集。
9、toArray();将集合转化为数组。
10、iterator();获取迭代器,用于取出集合中的元素,及否则返回flase。
集合中存储的都是集合的引用(地址)。
什么是迭代器:取出集合元素的方式。就是把取出方式定义在集合的内部,这样取出的方式就可以直接访问集合内部的元素,取出方式就被定义成内部类。因为每个容器的数据结构不同,所以取出的动作细节就会不一样,但是都有共性内容判断和取出。这样就可以将写共性抽取,就是Iterator规则,用iterator();方法获取集合的取出对象,迭代器中只要next()一次就要has.next()判断一次。
List:
List元素是有序的,元素可以重复,并且存在索引。
List集合中特有的方法:凡是可以操作角标的方法都是特有的方法。
增 add(index,element);addAll(index,Collection);
删remove(index);
改set(index,element)
查get(index);subList(from,to);listIterator();
indexof()获取元素的位置
List特有的迭代器List在集合中集合对象的方法操作集合中的元素,因为会发生ConcurrentModificationException异常,所以在迭代时,只能用迭代器的方式操作元素,可是Iterator的方法是有限的,只能对元素进行判断、删除、取出的操作。如果使用其他的操作就需要Iterator的子接口ListIteratori进行更改等更多的操作。该接口只能通过List集合的ListTerator()方法获得。ListIterator是Iterator的子接口。在迭代的时候不可以通过集合对象的方法操作集合中的元素。因为会发生ConcurrentModificationException异常。所以在迭代器中,只能用迭代器的方法操作元素,可是Interator的方法是有限的,只能对元素进行判断取出删除操作。如果要其他的操作就只能使用子类接口ListIterator。该方法通过List集合的listIterator方法获取,listIterayor特有的方法hasPrevious()
List常见的子类接口:(ArrayList、LinkedList、Vector)
ArrayList:底层的数据结构是数组,数据查询比较快,但是增删比较慢,线程不同步。延长是可变长度数组50%的。
LinkedList:底层的数据结构式链表。特点:增删比较快,但是查询会比较慢。
Vector:底层数组数据结构。特点:线程同步,被ArrayList代替。
Vector特有的方法:Enumeration en=v.element(); 枚举和迭代是一样的,因为枚举的方法和名称过长,枚举就被迭代所替代。
枚举Enumeration:
枚举就是Vector特有的取出方式。枚举和迭代器很相似,其实枚举和迭代就是一样的,因为枚举的名称和方法比较长,所以被迭代取代。
hasMoreElements()方法:
nextElement()方法:
Set:
Set元素是无需的(存入和取出的顺序不一定一致),元素不可以重复,不存在索引。Set集合的功能和Collection是一样的。
HashSet:数据结构是哈希表,线程是非同步的。
Set常见的子类对象:(HashSet、TreeSet)
HashSet:底层的数据结构式哈希表,线程是非同步的。
HashSet如何保证元素的唯一性:靠的是hashCode()和equals()方法,如果hashCode()值不一致则不调用equals()并存储对象,如果相等则就调用equals()方法,不相等就存入对象,如果相等就不存入对象。
注意:对于判断元素是否存在以及删除,依赖的方法是元素的hashcode()和equals()。 TreeSet:底层的数据结构是二叉树,可以对Set集合中的元素排序。
TreeSet如何保证元素唯一性依据:compareTo()方法返回return -1、0、1。
TreeSet排序的第一种方式:让元素自身具备比较性——元素需要实现Comparable接口,覆盖compareTo方法。这种方式也称为元素的自然排序,或者默认排序。
TreeSet第二种排序方式:当元素自身不具备比较性时,或者是具备的比较性不是所需要的, 这时就需要让集合自身具备比较性。在集合初始化时就有了比较方式。
注意:排序是当主要条件相同一定要判断下一个次要条件。
Map:
Map(K,V):该集合存储键值对,一对一对的存,每个键只能映射一个值,而且保证键的唯一性。
Map中基本方法:
1、添加:put(K key, V value) 将指定的值与此映射中的指定键关联(可选操作)。putAll(Map<? extends K,? extends V> m) 从指定映射中将所有映射关系复制到此映射中(可选操作)。
2、删除:clear() 从此映射中移除所有映射关系(可选操作)。remove(Object key)。
3、判断:containsKey(Object key) 如果此映射包含指定键的映射关系,则返回 true。 containsValue(Object value) 如果此映射将一个或多个键映射到指定值,则返回 true。 isEmpty();
4、获取:get(Object key);
size();
values();
enterSet();
keySet();
Map集合的子类对象(Hashtable、HashMap、TreeMap)。
Hashtable:底层是哈希表数据结构,不可以存入null键和null值,该集合线程同步。
HashMap:该集合底层是哈希表结构类型,允许存入null键和null值,该集合线程不同步。TreeMap:底层是二叉树数据结构。线程是同步的,可以给map集合中的键进行排序,实际上和Set很像,Set底层就是使用Map集合。
Map集合中特殊的取出方式:
keySet():将map集合中的键存入set集合中,因为set集合存在迭代器,所以可以迭代取出所有键,再根据get()方法获取键对应的值。
map集合的取出原理:将map集合转化成set集合,再通过迭代器取出。
Set<Map.Entry<k,v>>entrySet:将map集合中的映射关系存入到set集合中,而这个关系的数据类型就是Map.Entry。
System类:
System类包含一些有用的类字段和方法,它不能被实例化(因为此对象没有提供函数),System类中的属性和方法都是静态的。out代表标准输出,默认是控制台也就是屏幕;in代表标准输入,默认是键盘,设计键盘录入。
getProperties()方法;获取系统属性信息返回的Properties对象,因为Properties是Hashtable的子类,也就是Map集合的一个子类对象,这就可以通过map对象的方法取出该集合的元素。
Runtime对象,该类并没有提供构造函数。说明不可以new对象,这样该类中的方法都是静态的。但是该类中还有非静态的方法。类中存在方法获取本类对象,而且方法是静态的,返回值类型是本类类型。
StringBuffer类:
StringBuffer是字符串缓冲区是一个容器(容器的特点:长度是可变的;可以字节操作多个数据类型;最终通过toString方法变成字符串)
1、存储:StringBuffer append();将制定的内容作为参数追加到已有数据的后面方法调用链(方法放回来还是本类对象)。
StringBuffer insert(index,数据);可以将数据写入指定index位置。
2、删除:StringBuffer delete(start,end);删除容器中的内容,包含头不包含尾。
StringBuffer deleteCharAt(index);删除指定的字符。
delete (0,sb.length());清空缓冲区
3、获取:char chaAt(int index);
int indexOf(String str);
int lastIndexOf(String str);
int length();
String subString(int start,int,end);
4、修改:StringBuffer replase(start,end,修改后的内容);
void setCharAt(int index,char ch);
5、翻转:StringBuffer reverse();
6、将缓冲去内的内容存储到数组的指定位置中。
StringBuilder是jdk1.5版本以后存在的,它的功能和StringBuffer类似,不同之处在于StringBuffer是线程同步的,StringBuilder是线程不同步的,因为效率的问题建议使用StringBuilder。
java升级的因素:提高效率、简化代码书写、提高安全性。
基本数据类型对象包装类:
基本数据类型对象包装类(byte Byte、short Short、int Integer、long Long、boolean Boolean、float Float、double Double、char Character)。
基本数据类型对象包装类的作用——基本数据类型和字符串之间的转换:
基本数据类型转换为字符串:
1、基本数据类型+“”;
2、基本数据类型.toString(基本数据类型值) 如Integer.toString(34);将34转换成“34”; 将字符串转变为基本数据类型:基本数据包装类xxx a=Xxx.paseXxx(String);例如:int a=Integer.parseInt("123");boolean a=Boolea.parseBoolean("ture");
十进制转化为为其他进制:toBinaryString();toHexString();toOctalString()。
其他进制转化为十进制:parseInt(string,radix);Integer x=4;//自动装箱(等同于 new Integer
(4));x=x+2;利用x.intValue()方法自动拆箱变成int型。和2进行加法运算之后再装箱并赋值给x;但是当数值在byte范围内容,对于新特征,如果该数值已经存在则不会开辟新的空间,指向共同的值对象。
IO流:
io流用来处理设备之间的数据传输,java对数据的操作是通过流的方式,java用于操作流的对象都在io包中,流按操作数据分为两种:字节流(处理图片等)和字符流(解决乱码问题,基于字节流——字节字符映射表)。流按流向分为:输入流,输出流。
字节流的抽象基类:InputStream、OutputStream
字符流的抽象基类:Reader、Writer
注意:由这四个类派生出来的子类名称都是以其父类名为其子类名的后缀
那数据最常见的体现形式就是文件。
创建一个FileWriter对象,该对象一旦被初始化就必须明确被操作员的文件,而且该文件会被创建到指定的目录下,如果该目录下已经存在该文件,则文件将会被覆盖,变成空文件,其实该对象就是明确数据要存储的地方。
FileWriter常见的方法:
write();方法实际是将字符创写入流中。
flush();刷新流对象中的缓冲中的数据,并将数据数到目的地中。
close();关闭流资源,并在关闭之前刷新一次内部的缓冲的数据,将数据刷到目的地中。和flush的区别是flush刷新后,流可以继续使用,close刷新后会关闭流。
io异常处理方式:在外面建立引用在try内进行初始化。
泛型:jdk1.5版本以后出现的问题。用于解决安全问题,是一个机制。
泛型的好处:
1、将运行时期出现的问题转移到编译时期,方便程序员解决问题,让运行问题减少,这样的换程序将会更加安全。
2、避免了强制转换的麻烦。
网络编程
网络通信的要素:
ip地址:InetAddress(属于静态方法使用非静态方法调用),InetAddress是一个对象。 传输协议:udp(如聊天和视屏会议等):将数据源和目的地封装成数据包,不需要建立连接就可以就可以进行传输数据,每个数据包的大小限制在64k以内;因为没有建立连接时不可靠的传输协议;因为不需要建立连接,所以速度快。
tcp(下载); 建立连接。形成传输数据的通道(三次握手),在连接中进行大数据量传输,通过三次握手完成连接,是可靠的协议,必须建立连接,效率会稍低。
Socket:
Socket就是为网络服务提供一种机制,通信的两端都是Socket,网络通信的实质就是Socket之间的传输,数据在两个Socket之间通过io流传输数据。
DatagramSocket类:此类用来表示发送和接受数据的套接字。
利用udp发送数据的步骤:
①创建udp服务,通过DatagramSocket对象。
②确定数据并封装成包,DatagramPacket(byte[] buf,int length,IentAddress adress,int port)。 ③通过Socket服务,将已有的数据包通过sent()方法发送。
④close()方法关闭资源。
接受并处理udp传输的数据步骤:
①定义UdpSocket服务,通常会监听一个端口,其实就是给这个网络应用程序定义一个数字标识方便于明确哪些数据过来该应用程序可以处理。
②定义一个数据包,因为要存储接受到的字节数据。因为数据包对象中有更多功能可以提取数据,包中的不同数据信息。
③通过Socket服务中的Receive()方法将数据存入到已经定义好的数据包中。
④通过数据包对象的特有功能,将这些不同的数据取出,打印在控制台上。
⑤关闭资源程序。
同时拥有收数据的部分和发数据的部分。并且这两个部分用同时进行,这就需要使用多线程技术,一个线程控制收数据一个控制发数据,因为收和发步调是不一样,所以需要使用两个线程,并放在两个类中。
1、TCP分为客户端和服务端,客户端的对象是Socket,服务端的对象是ServerSocket。 客户端的创建:在创建对象时,就可以连接指定的主机,因为tcp是面向连接的。所以在建立socket服务时,就有服务端存在,并接成功,形成通路后,就在该通道上进行数据的传输。
创建tcp中socket的步骤:
1、创建socket对象并连接到指定的主机上。
2、获取socket服务中的输出流并指定输出的内容。
创建tcp服务端:
1、创建socket服务SeverSocket(),并监听一个端口。
2、获取连接过来的客户端,通过SeverSocket的accept()方法,此方法如果没有连接成功就会等,所以是一个阻塞式的方法。
3、客户端如果发来数据,那么服务端需要使用对应的客户端对象,并获取到该客户端对象的读取流来读取发过来的数据,并打印在控制台上。
4、关闭服务端(可选择的操作);
for循环:条件表达式一定要存在真假,for(初始化表达式只要合法的表达式;判断的语句;循环后的操作表达式)可以再表达式用逗号隔开,添加更多的表达式。无限循环的表现形式,for(;;){}for循环默认的判断语句为真。
正则表达式:
正则表达式的使用地方:
1、如果只想知道该字符是否是正确使用正则表达式,使用匹配。
2、想要将已有的字符串变成另一个字符串,使用替换。
3、想要将字符串变成多个字符串,使用分割。
4、想要拿到符合要求的字符串,获取。
使用正则的好处:简化匹配的复杂性。
内部类:
内部类就是将一个类定义在另一个类的里面,对里面那个类就称作做不类(内置类、嵌套类)。 内部类访问的特点:内部类可以直接访外部类中的成员,包括私有成员;而外部类要访问内部类中的成员必须要建立内部类的对象。
图像化编程:Graphical User Interface()
java.Awt:abstract window toolkit(抽象窗口工具包)。需要调用本地系统方法实现功能。属于中量级控件
javax.Swing:在AWT的基础上,建立的一套图形界面系统,其中提供了更多的组件,而且完全是由java实现。增强了可移植性,属轻量级控件
组件的继承关系
Component
Container
Window
Panel
Frame
Dialog
FileDialog
Button
Lable
Checkbok
TextComponent
TextArea
TextField
Container;为容器,是一个特殊的组件,该组件中可以通过add方法添加其他组件过来
布局:容器中组件的排放方式
常见的布局管理器:
FlowLayout(流式布布局管理器)
BorderLayout(边界布局管理器):东南西北中,Frame默认的布局管理器。
CardLayout(卡片布局管理器):选项卡。
GridBagLayout(网格布局管理器):不规则矩阵布局。
创建图形化界面步骤:
1、创建frame窗体。
2、对窗体进行基本的设置(大小 位置 布局)
3、定义组件
4、将组件通过窗体的add方法添加到窗体中
5、通过setVisible(ture)让窗体显示
事件监听机制
将监听器注册到事件源上---有监听锁监听的动作作用于事件源上---产生事件对象---将事件对象传给事件处理方式。
事件源:就是AWT包或者是swing包中的那些图形界面组件。
事件:每一个事件源都有自己的特有的对应事件和共性方法。
监听器:将可以触发某一事件的动作(不止一个动作)都已经封装到了监听器中。
事件源、事件、事件监听器都是java中已经定义好了的,直接获取对象来就可以使用的,我们需要做的事就是对产生的动作进行处理。
继承WindowListener时候需要覆盖其中的7个方法,但是虽然只是使用一个方法但是其他的方法我们也需要复写所有的方法。但是WindowListener的子类WindowAdapter已经实现了WindowListener接口,并且覆盖了其中的所有方法。那么就我们继承WindowAdapter覆盖我们需要的方法就可以。