前言
(1)编程:也就是人和计算机之间交流的过程,让计算机为解决某个问题而使用某种程序设计语言编写程序代码,并最终得到结果的过程。
(2)写代码的要诀:明确需求->分析思路->确定步骤->代码实现。
(3)学习新技术要诀:技术是什么->技术特点->技术如何使用->技术什么时候用。
第一部分:Java
1:Java概述
(1)Java是SUN(Standford University Network,即斯坦福大学网络公司)推出的一门面向对象的高级编程语言。目前成为了Web应用领域的首选开发语言。具有简单易学、完全面向对象、安全可靠、跨平台的编程语言。
* 1991年SUN公司James Gosling开发了名称为Oak的语言,目的用于控制嵌入在有线电视交换盒、PDA等微处理器。
* 1994年Oak语言更名为Java。
* 1995年正式推出Java首个版本。
(2)技术框架
*J2SE(JDK5.0之前)| JavaSE(JDK5.0之后):完成桌面应用程序的开发,是其它两者的基础。以后不管从事Android开发或者是物联网+云计算的开发等都是建立在JavaSE基础上的,因此该技术是java的最核心技术。
*J2EE(JDK5.0之前)| JavaEE(JDK5.0之后):开发企业环境下的应用程序,主要针对web程序开发。该技术体系中包含的技术如Servlet、JSP等,主要针对于Web应用程序开发。
*J2ME(JDK5.0之前)| JavaME(JDK5.0之后):开发电子消费产品和嵌入式设备,如手机中的程序。以前在手机开发上应用的也比较多,但是随着智能手机的发展,现在手机应用程序(比如Android程序)的开发已经不再使用该技术。
(3)何为跨平台,原理又是什么呢?
* 跨平台:通过Java语言编写的相关程序,可以一次编译,多系统平台运行。
* 原理:Java应用程序通过JVM在OS平台运行,只要OS平台可以安装相应JVM,即可运行Java应用程序(关键点在于系统是否可以安装相应的JVM)。
(4)何为JRE(Java Runtime Environment,即Java 运行环境)?
JRE包括Java虚拟机JVM(Java Virtual Machine)+Java程序所需的核心类库等,如果仅运行一个已经开发完成的Java程序,系统只需安装JRE 。
(5)何为JDK(Java Development Kit,即Java 开发工具包)?
JDK是提供给Java开发人员使用的,包含Java的开发工具(编译工具javac.exe,打包工具jar.exe等)和JRE。JDK中包含JRE,安装JDK了无需再安装JRE。
(6)何为VM,何为JVM(Java Virtual Machine,即Java虚拟机)?
* VM,即虚拟机。当我们在虚拟机中进行软件评测时,可能系统一样会崩溃,但崩溃的仅是虚拟机上的OS,而不是物理计算机上的OS,使用虚拟机的Undo(恢复)功能,可以立即恢复虚拟机到安装软件之前的状态。
* JVM是运行所有Java程序的抽象和虚拟的计算机,它是Java语言的运行环境,JVM读取并处理编译过的与平台无关的字节码(.class)文件。由于Java编译器针对JVM产生.class字节码文件,因此具有平台独立性。Java解释器负责将JVM的代码在特定的平台上运行。Java虚拟机是不跨平台的(针对特定的平台具有不同类型的JVM)。
(7)Java运行机制?
* 编译(Java源文件)->运行(字节码文件)
* javac:javac 文件名.文件后缀名
javac负责编译的部分,当执行javac时,会启动java的编译器程序。对指定扩展名的.java文件进行编译,生成了jvm可以识别的字节码文件。即class文件,也就是java的运行程序。
* java 类名
java负责运行的部分。会启动jvm,加载运行时所需的类库,并对class文件进行执行。 一个文件要被执行,必须要有一个执行的起始点,这个起始点就是main函数。
(8)配置环境变量
* 新建JAVA_HOME变量(JDK安装目录);
* 新建CLASSPATH变量(类加载路径);
* 修改PATH变量(bin目录);
* 在定义classpath环境变量时,如果没有定义环境变量classpath,java启动jvm后,会在当前目录下查找要运行的类文件; 如果指定了classpath,那么会在指定的目录下查找要运行的类文件。 还会在当前目录找吗?两种情况: 1):如果classpath的值结尾处有分号,在具体路径中没有找到运行的类,会默认在当前目录再找一次。 2):如果classpath的值结果出没有分号,在具体的路径中没有找到运行的类,不会再当前目录找。 一般不指定分号,如果没有在指定目录下找到要运行的类文件,就报错,这样可以调试程序。
(9)Java注意事项
* Java语言严格区分大小写;
* 一个Java源文件可定义多个Java类,但最多有一个类被定义为public;
* 一个源文件中包含n个Java类时,编译后会生成n份的字节码文件,即每个类都会生成一份单独的class文件,且字节码文件名和其对应的类名相同;
2:Java基础
(1)代码注释(不能嵌套使用)
* 单行://
* 多行:/* */
* 文档注释:/* /
(2)Java基础概念
* 关键字:某种语言赋予了特殊含义的单词。
* 保留字:没有赋予特殊含义,但准备日后使用的单词。
* 标识符:程序中自定义的名词。
#标识符命名规则:
a. 由字母(中文,日文等)、数字、下划线、$组成,不能以数字开头 ;
b. 大小写敏感 ;
c. 不得使用java中的关键字和保留字 ;
d. 不得用Java API里面的类名作为自己的类名;
* 常量:定义之后,在程序中的不会变化的数据。
* 变量:内存中的一个存储空间,用于存储常量数据。该区域有自己的名称(变量名)和类型(数据类型),并且该数据可以在同一类型范围内不断变化;
#作用和特点:因为有些数据不确定,所以确定该数据的名词和存储空间。方便运算,空间重复使用。
#作用范围:定义开始到定义它的代码块结束;
#定义三要素:数据类型 变量名称 变量的初始化值
* 局部变量:不是声明在类体括号里面的变量。使用前必须初始化值,没有默认初始化值,作用域是从定义开始到定义它的代码块结束;
* 成员变量:在方法体外,类体内声明的变量,又称字段(Field)或全局变量;(Java中没有全局变量,由于Java是面向对象语言,所有变量都是类成员) ,成员变量的作用域是整个类中;
(3)基本数据类型
* 8个基本数据类型:byte(1)、short(2)、int(4)、long(8)、float(2)、double(4)、char(2)、boolean(1)
* 引用数据类型:数组、类、接口。
* 级别低到高:byte/short/char(这三个在计算时会转换为int类型)->int->float->long->double
* 自动类型转换(隐式类型转换):级别由低到高;
* 强制类型转换(显示类型转换):级别由高到低(需要括号强制转换);
(4)运算符
* 算术运算符(+ 、— 、* 、/ 、%、++、--)
* 赋值运算符(= 、+=、-=、*=、/=,、%=)
* 关系运算符(>、>=、<、<=、!=)
* 条件运算符(&&、||、!)
* 位运算符(&、|、^、~、>>、<<、<<<、>>>)
* 三目运算符(x?y:z)
(5)语句
* if else:当判断固定个数的值时候使用,也可转换为switch语句
* switch (case break default):多条件判断时可以使用,效率比较高。
a.break是可以省略的,如果省略了就一直执行到遇到break为止;
b.switch 后面的小括号中的变量应该是byte,char,short,int类型中一种;
c.default可以写在switch结构中的任意位置;如果将default语句放在了第1行,则不管expression与case中的value是否匹配,程序都会从default开始执行直第一个break出现。
* while:事先不需要知道循环执行多少次;
* do while:同上,只是至少要执行一次(先做,后判断);
* for:需要知道循环次数;或者增强for遍历;
*for-each(JDK5开始):增强for循环,用于数组和Iterable对象遍历输出。
* break:作用于switch、循环语句,用于跳出或者结束。break语句单独存在时,下面不要定义其他语句,因为执行不到,编译会失败。当循环嵌套时,break只跳出当前所在循环。要跳出嵌套中的外部循环,只要给循环起名字即可,这个名字称之为标号。
* continue:只作用于循环结构,继续循环用的。结束本次循环,继续下次循环。该语句单独存在时,下面不可以定义语句,执行不到。
(6)方法(函数)
* 为了提高代码的复用性,可以将其定义成一个单独的功能,该功能的体现就是java中的函数,函数就是体现之一。
* 格式:访问修饰符 返回值类型 函数名(参数类型 形式参数1,,,){ 执行语句;
return 返回值;}
* 格式属性
a.访问控制符:访问控制符限定方法的可见范围,或者说是方法被调用的范围。方法的访问控制符有四种,按可见范围从大到小依次是:public、protected,无访问控制符,private。其中无访问控制符不书写关键字即可。具体的范围在后续有详细介绍。
b.形式参数:在方法被调用时用于接收外界输入的数据。
c.实参:调用方法时实际传给方法的数据。
d.返回值:方法在执行完毕后返还给调用它的环境的数据。
e.返回值类型:事先约定的返回值的数据类型,如无返回值,必须给出返回类型 void。
f.方法签名:方法名和方法的参数列表(能区别方法);//最关键的
g.java语言中调用方法:对象名.方法名(实参列表)。
h.实参的数目、数据类型和次序必须和所调用方法声明的形参列表匹配。
i.return 语句终止方法的运行并指定要返回的数据。
* 当函数没有具体的返回值时,返回的返回值类型用void关键字表示。 如果函数的返回值类型是void时,return语句可以省略不写的,系统会帮你自动加上。 return的作用:结束函数,结束功能。
* 方法特点:实现独立的功能,必须定义在类里面,它只有被调用才会执行,它可以被重复使用,方法结束后方法里的对象失去引用。
* 方法作用:用于定义功能、用于封装代码提高代码的复用性。函数中只能调用函数,不能定义函数。
* 主函数:保证该类的独立运行、因为它是程序的入口、因为它在被jvm调用。
* 方法重载:在一个类中,如果出现了两个或者两个以上的同名函数,只要它们的参数的个数,或者参数类型不同,即可称为方法重载。和返回值类型没关系,只看参数列表。和方法覆写不一样,方法覆写的方法名和参数列表都必须一样。
(7)可变参数
* java5开始出现了可变参数,是对java方法及数组的拓展,方法中可以接受的参数不再是固定个数的,而是随着具体需求传递的多少来决定。
* 格式:返回值类型 方法名(参数类型 … 形式参数){ }
* 特点:只能出现在参数列表的最后,… 位于变量类型和变量名之间,前后有无空格都可以,调用可变参数的方法时,编译器为该可变参数隐含创建一个数组,在方法体中以数组的形式访问可变参数。
(8)数组
* 数组(Array):用于存储同一类型数据的一个容器。它是Java 语言中内置的一种基本数据存储结构,通俗的理解,就是一组数的集合,目的是用来一次存储多个数据。数组是程序中实现很多算法的基础,可以在一定程度上简化代码的书写。
* 好处:数组里的每个元素都有编号,编号从0开始,并且依次递增,方便操作这些元素。数组用于封装数据,就是一个具体的实体。
* 使用Java数组必须先声明数组,再给该数组分配内存。数组对应在内存中一段连续空间。 数组元素必须是相同数据类型,也可以是引用数据类型,但是同一个数组中的元素必须是同一类数据类型。
* 三种格式:
元素类型[] 变量名 = new 元素类型[元素的个数]
元素类型[] 变量名 = {元素1,元素2...}
元素类型[] 变量名 = new 元素类型[]{元素1,元素2...}
* 初始化=定义数组+分配空间+赋值
(9)Java内存分布
* 五片内存:寄存器、本地方法区、方法区、栈、堆
* 栈:存储的都是局部变量 ( 函数中定义的变量,函数上的参数,语句中的变量 ),只要数据运算完成所在的区域结束,该数据就会被释放。
* 堆:用于存储数组和对象,也就是实体。啥是实体啊?就是用于封装多个数据的。每一个实体都有内存首地址值、堆内存中的变量都有默认初始化值(因为数据类型不同,值也不一样)、具有垃圾回收机制。
(10)Arrays工具类(数组的辅助工具类)
* static int binarySearch(type[] a, type key):使用二分搜索法来搜索key元素在数组中的索引;若a数组不包括key,返回负数。(该方法必须已按升序排列后调用)。
* static int binarySearch(type[] a, int fromIndex, int toIndex, type key) :使用二分搜索法来搜索key元素在数组中从fromIndex到toIndex的索引;若a数组不包括key,返回负数。(该方法必须已按升序排列后调用)。
* static boolean[] copyOf(type[] original, int newLength) 复制指定的数组。
* static byte[] copyOfRange(type[] original, int from, int to) 将数组的指定范围复制到一个新数组。
* static boolean equals(type[] a, type[] a2) 如果两个数组长度相等和元素一一相等,则返回 true 。
* static void fill(type[] a, type val) 将a数组所有元素都赋为val。
* static void fill(type[] a, int fromIndex, int toIndex, type val) 将a数组从formIndex 到tiondex索引之间的元素都赋为val。
* static void sort(type[] a) //sort(int[] arr)对指定的数组按数字升序进行排序。
* static void sort(type[] a, int fromIndex, int toIndex) 对指定数组的从formIndex 到tiondex索引之间的元素按数字升序进行排序。
* static String toString(type[] a) 返回指定数组内容的字符串表示形式。多个数组元素之间用英文逗号或空格隔开。(其余可查API帮助文档)。
3:面向对象
(1)何为面向对象?
* 面向对象(Object-Oriented,简称OO)是一种常见的程序结构设计方法,将相关的数据和方法放在一起,组合成一种新的复合数据类型,然后使用新创建的复合数据类型作为项目的基础。它是一个很抽象的概念,它相对面向过程而言。 当然过程与对象都是一种解决问题的思想。 但是面向过程强调的是功能行为,一种过程,先干啥,再干啥。而面向对象将功能封装到对象里,强调的是具备某功能的对象。万物皆对象!
* 思想: 封装(Encapsulation); 继承(Inheritance); 多态(Polymorphism)。
* 特点:将复杂的事情简单化。面向对象将以前的过程中的执行者,变成了指挥者。面向对象这种思想是符合现在人们思考习惯的一种思想。
* UML图:
* 继承
* 实现
*依赖
* 关联
* 聚合
* 组合
(2)何为类(class)?
* 类(class)是Java 语言的最小编程单位,是设计和实现Java 程序的基础。类是一组事物共有特征和功能的描述。类是对于一组事物的总体描述,是按照面向对象技术进行设计时最小的单位,也是组成项目的最基本的模块。类只包含框架结构,而不包含具体的数据。所以类代表的是总体,而不代表某个特定的个体。
* 类定义:
[修饰符] class 类名{
1~n个构造方法(默认会有一个无参构造方法);
0~n个字段;
0~n个方法 ;
}
* 对象 =属性 +方法; 状态==属性; 功能、行为==方法;
(3)匿名对象:当对方法只进行一次调用的时候,可以使用匿名对象。当对象对成员进行多次调用时,不能使用匿名对象。必须给对象起名字。
* 特点:
a. 对方法或字段只进行一次调用时;
b. 可作为实际参数进行传递;
c. 只在堆里面开辟存储区域,
d. 只能使用一次, 使用完就被销毁了;
e. new Person();表示匿名对象,没有名字的对象
f. new Person().age = 17;//使用一次之后就被销毁了
(4)构造方法:用来构造类的实例,每个类都默认有一个无参的构造方法,使用new调用自动调用。给类中的字段进行初始化,可以用来创建具体的对象。所有对象创建时,都需要初始化才可以使用。
特点:1:该函数的名称和所在类的名称相同。
2:不需要定义返回值类型。
3:该函数没有具体的返回值。
(5)static关键字
* 特点:随着类的加载而加载 、优先于对象存在 、被所有对象所共享 、可以直接被类名调用,静态方法中不能使用this,super关键字。
* 静态方法只能访问静态成员 、但是非静态成员可以访问静态成员; 静态方法中不可以使用this,super关键字 、主方法(main)是静态的(可以利用类名去调用静态的main方法,但是会陷入死循环,导致内存溢出,jvm自动停止!)
* 可修饰字段,方法。 用static 修饰的成员表示它属于这个类共有,而不是属于该类的单个实例。static 修饰的字段 == 类字段 、static 修饰的方法 == 类方法。没使用static修饰的字段和方法,成员属于类的单个实例, 不属于类。没有static 修饰的字段 == 实例字段 、没有static 修饰的方法 == 实例方法。
* 类和实例访问字段和方法的语法:
访问类成员: 类.字段 类.方法
访问实例成员: 实例.字段 实例.方法
(6)this关键字
特点:this表示当前对象(当前正在调用实例成员的对象 )。用于方法间的相互调用、this.字段、构造器中相互调用,但是此时this([参数])必须写在构造方法第一行。this不能用在static修饰的方法里和static修饰的代码块里。
(7)构造代码块和构造函数区别?
* 构造代码块:是给所有的对象进行初始化,也就是说,所有的对象都会调用一个代码块。只要对象一建立。就会调用这个代码块。
* 构造函数:是给与之对应的对象进行初始化。它具有针对性。
* 静态代码块、构造代码块、构造函数同时存在时的执行顺序:静态代码块->构造代码块->构造函数。
(8)构造函数和一般函数区别?
* 两个函数定义格式不同。
* 构造函数是在对象创建时,就被调用,用于初始化,而且初始化动作只执行一次。一般函数,是对象创建后,需要调用才执行,可以被调用多次。
(9)成员变量和静态变量的区别?
a. 成员变量所属于对象。所以也称为实例变量。静态变量所属于类。所以也称为类变量。
b. 成员变量存在于堆内存中。静态变量存在于方法区中。
c. 成员变量随着对象创建而存在。随着对象被回收而消失。静态变量随着类的加载而存在。随着类的消失而消失。
d. 成员变量只能被对象所调用 。静态变量可以被对象调用,也可以被类名调用。所以,成员变量可以称为对象的特有数据,静态变量称为对象的共享数据。
(10)new一个对象时,java内存发生了什么变化(例如Cat c=new Cat())?
a. 先将硬盘上指定位置的Cat.class文件加载进内存。
b. 执行main方法时,在栈内存中开辟了main方法的空间(压栈-进栈),然后在main方法的栈区分配了一个变量c。
c. 在堆内存中开辟一个实体空间,分配了一个内存首地址值。
d. 在该实体空间中进行属性的空间分配,并进行了默认初始化。
e. 对空间中的属性进行显示初始化。
进行实体的构造代码块初始化。
f. 调用该实体对应的构造函数,进行构造函数初始化。
g. 将首地址赋值给c ,c变量就引用了该实体。
(11)封装
* 封装是指隐藏对象的属性和实现细节,仅对外提供公共访问方式。把描述对象的状态用字段表示,描述对象的行为用方法表示,把字段和方法定义在一个类中,并保证外界不能任意更改其内部的字段值,也不允许任意调动其内部的功能方法。
* 通常将类中的成员变量私有化(private),通过对外提供方法(setXxx,getXxx),可对该变量(xxx)进行访问。boolean 类型的变量没有getXX,只有 isXX。
* 将变化隔离;便于使用;提高重用性;安全性。
(12)访问修饰符
private :本类内部可以访问,不能继承到子类;
default :什么都不写,包访问权限,本类内部可以访问,同包其他类也可以访问,同包可继承;
protected: 子类访问权限,本类内部可以访问,不同包的子类也可以访问,同包其他类也可以访问,能继承到子类;
public:公共访问权限,任何地方都可以访问,能继承到子类;
(13)类设计分析
a. 根据要求写出类所包含的字段;
b. 所有的字段都必须私有化;
c. 封装之后的字段可通过setter和getter设值和取得;
d. 按需求可添加若干构造方法;
e. 根据需求可添加相应的方法;
f. 类中的所有方法都不要直接处理(输出打印),而是交给调用者去处理。
(14)GoF设计模式之单例设计模式
* 单例设计模式:保证一个类在内存中的对象唯一性。Runtime()方法就是单例设计模式进行设计的。
* 思想:不让其他程序创建该类对象 、在本类中创建一个本类对象、对外提供方法,让其他程序获取这个对象。有恶汉模式和懒汉模式两种。
* 实现步骤:
1.因为创建对象都需要构造函数初始化,只要将本类中的构造函数私有化,其他程序就无法再创建该类对象;
2.就在类中创建一个本类的对象;
3.定义一个方法,返回该对象,让其他程序可以通过方法就得到本类对象。
代码体现:私有化构造函数->创建私有并静态的本类对象->定义公有并静态的方法,返回该对象。
(15)继承
* 继承是一种从一般到特殊的关系。反映一般事物特性的类,然后在此基础上反映出特殊事物的类;
* 提高了代码的复用性;让类与类之间产生关系,有了这个继承关系才有了多态的特性;Java语言中只支持单继承(有别于C++语言);因为多继承容易带来安全隐患(父类多了, 功能相同的话,就会出现调用不确定性吗,覆写一个方法,到底覆写的谁的?);接口可以实现多继承 ;Java支持多层继承,object是每个类的超类,实现树形结构。
* 格式: [修饰符] class SubClass extends SuperClass
* 父类的私有成员和父类的构造方法不能被继承;Java只支持单继承,不支持多继承;一个类有且只有一个直接父类; 一个类没显示的继承其他的一个类的时候,默认的直接父类就是Object类; 一旦一个类显示的继承了其他的一个类的时候,此时默认的直接父类Object就会被取消; Java里一个类只能有一个直接父类;java.lang.Object是所有类的父类,Object要么是直接父类要么是间接父类。
* 子类对象实例化过程:子类对象在实例化之前必须首先调用父类中的构造方法之后再调用自身的构造方法。
* 对于一个继承体系的使用,查阅顶层父类中的内容,创建最底层子类的对象。
* 继承的成员特点:
#成员变量:当子父类中出现一样的属性时,子类类型的对象,调用该属性,值是子类的属性值。如果想要调用父类中的属性值,需要使用一个关键字:super(This:代表是本类类型的对象引用,Super:代表是子类所属的父类中的内存空间引用)。子父类中通常是不会出现同名成员变量的,因为父类中只要定义了,子类就不用在定义了,直接继承过来用就可以了。
#成员函数:当子父类中出现了一模一样的方法时,建立子类对象会运行子类中的方法。好像父类中的方法被覆盖掉一样。所以这种情况,是函数的另一个特性:覆盖(复写,重写) ,当一个类的功能内容需要修改时,可以通过覆盖来实现。
#构造函数:发现子类构造函数运行时,先运行了父类的构造函数。子类的所有构造函数中的第一行,其实都有一条隐身的语句super()。super(): 表示父类的构造函数,并会调用于参数相对应的父类中的构造函数。而super():是在调用父类中空参数的构造函数。因为子类继承父类,会继承到父类中的数据,所以必须要看父类是如何对自己的数据进行初始化的。所以子类在进行对象初始化时,先调用父类的构造函数,这就是子类的实例化过程。
* 子类中所有的构造函数都会默认访问父类中的空参数的构造函数,因为每一个子类构造内第一行都有默认的语句super();如果父类中没有空参数的构造函数,那么子类的构造函数内,必须通过super语句指定要访问的父类中的构造函数。如果子类构造函数中用this来指定调用子类自己的构造函数,那么被调用的构造函数也一样会访问父类中的构造函数。
* super()和this()是否可以同时出现的构造函数中。两个语句只能有一个定义在第一行,所以只能出现其中一个。因为super()或者this()都是调用构造函数,构造函数用于初始化,所以初始化的动作要先完成。
* 子类复写父类方法时,子类方法的权限必须大于等于父类方法权限可以实现继承。否则,编译失败。覆盖时,要么都静态,要么都不静态。 (静态只能覆盖静态,或者被静态覆盖);
(16)final关键字
* 这个关键字是一个修饰符,可以修饰类,方法,变量。被final修饰的类是一个最终类,不可以被继承。被final修饰的方法是一个最终方法,不可以被覆盖。被final修饰的变量是一个常量,只能赋值一次。
* 加了final,程序更为严谨。常量名称定义时,有规范,所有字母都大写,如果由多个单词组成,中间用 _ 连接。
(17)抽象类(abstract)
* 抽象:不具体,看不明白。抽象类表象体现。在不断抽取过程中,将共性内容中的方法声明抽取,但是方法不一样,没有抽取,这时抽取到的方法,并不具体,需要被指定关键字abstract所标示,声明为抽象方法。抽象方法所在类一定要标示为抽象类,也就是说该类需要被abstract关键字所修饰。
* 特点:
1:抽象方法只能定义在抽象类中,抽象类和抽象方法必须由abstract关键字修饰(可以描述类和方法,不可以描述变量)。
2:抽象方法只定义方法声明,并不定义方法实现。
3:抽象类不可以被创建对象(实例化)。
4:只有通过子类继承抽象类并覆盖了抽象类中的所有抽象方法后,该子类才可以实例化。否则,该子类还是一个抽象类。
* 抽象类可以定义非抽象方法。抽象类和一般类没有太大的区别,都是描述事物,只不过抽象类在描述事物时,有些功能不具体。所以抽象类和一般类在定义上,都是需要定义属性和行为的。只不过,比一般类多了一个抽象函数。而且比一般类少了一个创建对象的部分。
* 抽象类中是有构造函数,用于给子类对象进行初始化。
* 抽象关键字abstract和final , private , static不可以共存。
(18)GoF设计模式之模板方法设计模式
* 模板方法设计模式:当功能内部一部分实现时确定,一部分实现是不确定的。这时可以把不确定的部分暴露出去,让子类去实现。
* 代码实现:
(19)接口(interface)
* 关键字interface定义的;接口中包含的成员,最常见的有全局常量、抽象方法;接口中的成员都有固定的修饰符。
*格式: 成员变量:public static final 成员方法:public abstract
interface Inter{
public static final int x = 3; public abstract void show();
}
* 接口中有抽象方法,说明接口不可以实例化;接口的子类必须实现了接口中所有的抽象方法后,该子类才可以实例化,否则,该子类还是一个抽象类; 类与类之间存在着继承关系,类与接口中间存在的是实现关系; 继承用extends ;实现用implements ; 接口和类不一样的地方,就是接口可以被多实现,这就是多继承改良后的结果;java将多继承机制通过多现实来体现;一个类在继承另一个类的同时,还可以实现多个接口。所以接口的出现避免了单继承的局限性;还可以将类进行功能的扩展;其实java中是有多继承的;接口与接口之间存在着继承关系,接口可以多继承接口。
* 特点:接口是对外提供的规则;接口是功能的扩展;接口的出现降低了耦合性。
(20)抽象类与接口区别?
* 共性:都是不断向上抽取的结果。
* 区别:
1:抽象类只能被继承,而且只能单继承。接口需要被实现,而且可以多实现。
2:抽象类中可以定义非抽象方法,子类可以直接继承使用。接口中都是抽象方法,需要子类去实现。
3:抽象类使用的是 is a 关系。 接口使用的 like a 关系。
4:抽象类的成员修饰符可以自定义。接口中的成员修饰符是固定的。全都是public的。
(21)多态
* 函数本身就具备多态性,某一种事物有不同的具体的体现。父类引用或者接口的引用指向了自己的子类对象。
* 多态提高了程序的扩展性;继承的父类或接口一般是类库中的东西,(如果要修改某个方法的具体实现方式)只有通过子类去覆写要改变的某一个方法,这样在通过将父类的应用指向子类的实例去调用覆写过的方法就行。当父类引用指向子类对象时,虽然提高了扩展性,但是只能访问父类中具备的方法,不可以访问子类中特有的方法。(前期不能使用后期产生的功能,即访问的局限性);多态的前提:必须要有关系,比如继承、或者实现。通常会有覆盖操作。
* 多态的出现思想上也做着变化,以前是创建对象并指挥对象做事情。有了多态以后,我们可以找到对象的共性类型,直接操作共性类型做事情即可,这样可以指挥一批对象做事情,即通过操作父类或接口实现。
* 引用变量类型转换:向上转型(子类→父类):(自动完成) ;向下转型(父类→子类):(强制完成) ;对象的类型和类必须有继承关系;
* instanceof关键字:判断对象是否实现了指定的接口或继承了指定的类。
* 多态在子父类中的体现特点
#成员变量:在多态中,子父类成员变量同名。 在编译时期->参考的是引用型变量所属的类中是否有调用的成员。(编译时不产生对象,只检查语法错误) ; 运行时期->也是参考引用型变量所属的类中是否有调用的成员。无论编译和运行,成员变量参考的都是引用变量所属的类中的成员变量。 成员变量 --- 编译运行都看 = 左边。
#成员函数: 编译时期->参考引用型变量所属的类中是否有调用的方法。 运行时期->参考的是对象所属的类中是否有调用的方法。因为在子父类中,对于一模一样的成员函数,有一个特性:覆盖。成员函数,编译看引用型变量所属的类,运行看对象所属的类;成员函数 --- 编译看 = 左边,运行看 = 右边。
#静态函数:编译时期->参考的是引用型变量所属的类中是否有调用的成员。运行时期->也是参考引用型变量所属的类中是否有调用的成员。因为静态方法,其实不所属于对象,而是所属于该方法所在的类。调用静态的方法引用是哪个类的引用调用的就是哪个类中的静态方法。静态函数 --- 编译运行都看 = 左边。
(22)基本数据类型的包装类:除了Integer和Character定义的名称和对应的基本类型差异大,其他六种都是将首字母大写。Integer,Byte,Float,Double,Short,Long都是Number类的子类;Character和Boolean都是Object直接子类; 8个类都是final修饰的(不可被继承)。
(23)基本数据类型和包装类相互转换:jdk1.5开始出现的特性。
自动装箱:可把一个基本类型变量直接赋给对应的包装类对象或则Object对象 。
自动拆箱:允许把 包装类对象直接赋给对应的基本数据类型。
(24)基本类型和String之间的转换
String → 基本类型:除了Character外所有的包装类提供parseXxx(String s)静态方法,用于把一个特定的字符串转换成基本类型变量;
基本类型 → String:String 类有静态方法valueOf(),用于将基本类型的变量转换成String类型。
(25)java.lang.Object类
* 所有类的直接或者间接父类,Java认为所有的对象都具备一些基本的共性内容,这些内容可以不断的向上抽取,最终就抽取到了一个最顶层的类中的,该类中定义的就是所有对象都具备的功能。所有类的公共父类,一旦一个类没有显示地继承一个类则其直接父类一定是Object。
* 常见方法:
1:boolean equals(Object obj):用于比较两个对象是否相等,其实内部比较的就是两个对象地址。如果根据 equals(Object) 方法,两个对象是相等的,那么对这两个对象中的每个对象调用 hashCode 方法都必须生成相同的整数结果; 而根据对象的属性不同,判断对象是否相同的具体内容也不一样。所以在定义类时,一般都会复写equals方法,建立本类特有的判断对象是否相同的依据;
2:String toString():将对象变成字符串;默认返回的格式:类名@哈希值 = getClass().getName() + '@' + Integer.toHexString(hashCode()) 为了对象对应的字符串内容有意义,可以通过复写,建立该类对象自己特有的字符串表现形式。
3:Class getClass():获取任意对象运行时的所属字节码文件对象。
4:int hashCode():返回该对象的哈希码值。支持此方法是为了提高哈希表的性能。将该对象的内部地址转换成一个整数来实现的。通常equals,toString,hashCode,在应用中都会被复写,建立具体对象的特有的内容。
(26)代码块分类:
#普通代码块: 直接定义在方法或语句中定义的代码块;
#构造代码块:直接写在类中的代码块;优先于构造方法执行,每次实例化对象之前都会执行构造代码块。
#静态代码块:使用static 修饰的构造代码块;优先于主方法执行,优先于构造代码块执行,不管有创建多少对象,静态代码块只执行一次,可用于给静态变量赋值;
#同步代码块(线程同步的时候讲解)。
#代码块里变量的作用域: 只在自己所在区域(前后的{})内有效;
(27)内部类:内部类可以直接访问外部类中的成员。而外部类想要访问内部类,必须要建立内部类的对象。
(28)GoF设计模式之简单工厂设计模式
* 构建一个工厂出来,在里面进行生产,用的时候直接拿;屏蔽不同子类实现的差异,提高代码的可拓展性和可维护性;
* 代码实现
(29)GoF设计模式之适配器设计模式
* 使用一个现成的类,但是它的接口不完全符合你的需求,我只想要它其中的一个方法,不想覆写其他的方法。比如,窗体有变大,变小,关闭的行为,但是我现在只需要关闭行为;
* 代码实现:
(30)枚举类
* 使用enum声明,默认直接继承了java.lang.Enum类,而不是Object类; 枚举类的对象是固定的,实例个数有限,不可以再new( ),枚举对象后可以跟()。枚举元素必须位于枚举类体中的最开始部分,枚举元素后要有分号与其他成员分隔。枚举类的构造方法的权限修饰符默认是private; 一旦枚举对象后面加上{},那么该对象实际是枚举匿名内部类对象; 所有枚举类都提供一个静态的values()方法(返回该枚举类所有对象组成的数组),便于遍历所有枚举对象;所有枚举类都提供一个静态的valueOf(String name)方法, 返回枚举类中对象名等于 name的对象。
* 枚举不可以new();即便是反射也不可以。
4:高级部分
(1)异常:就是不正常。程序在运行时出现的不正常情况。其实就是程序中出现的问题。这个问题按照面向对象思想进行描述,并封装成了对象。因为问题的产生有产生的原因、有问题的名称、有问题的描述等多个属性信息存在。当出现多属性信息最方便的方式就是将这些信息进行封装。异常就是java按照面向对象的思想将问题进行对象封装。这样就方便于操作问题以及处理问题。出现的问题有很多种,比如角标越界,空指针等都是。就对这些问题进行分类。而且这些问题都有共性内容比如:每一个问题都有名称,同时还有问题描述的信息,问题出现的位置,所以可以不断的向上抽取。形成了异常体系。
* 异常体系:
--------java.lang.Throwable: Throwable:可抛出的。
|--Error:错误,一般情况下,不编写针对性的代码进行处理,通常是jvm发生的,需要对程序进行修正。
|--Exception:异常,可以有针对性的处理方式 。
* 具有可抛性,被throws和throw两个关键字所操作。
* 格式:throws 异常类名,异常类名...
* 关键字:try...catch...finally
* 原则:功能抛出几个异常,功能调用如果进行try处理,需要与之对应的catch处理代码块,这样的处理有针对性,抛几个就处理几个。try对应多个catch时,如果有父类的catch语句块,一定要放在下面。
* throw和throws关键字区别:throw用于抛出异常对象,后面跟的是异常对象;throw用在函数内。throws用于抛出异常类,后面跟的异常类名,可以跟多个,用逗号隔开。throws用在函数上。通常情况上函数内容如果有throw,抛出异常对象,并没有进行处理,那么函数上一定要声明,否则编译失败。但是也有特殊情况。
* 异常分两种: a.编译时异常:只要是Exception及其子类都是编译时被检测的异常。 b.运行时异常:其中Exception有一个特殊的子类RuntimeException,以及RuntimeException的子类是运行异常,也就说这个异常是编译时不被检查的异常。 区别:编译被检查的异常在函数内被抛出,函数必须要声明,否编译失败。 声明的原因是需要调用者对该异常进行处理。运行时异常如果在函数内被抛出,在函数上不需要声明。不声明的原因是不需要调用者处理,运行时异常发生,已经无法再让程序继续运行,所以,不让调用处理的,直接让程序停止,由调用者对代码进行修正。
*自定义异常:当开发时,项目中出现了java中没有定义过的问题时,这时就需要我们按照java异常建立思想,将项目的中的特有问题也进行对象的封装。
自定义步骤:
a. 定义一个子类继承Exception或RuntimeException,让该类具备可抛性(既可以使用throw和throws去调用此类)。
b. 通过throw 或者throws进行操作。
* finally很有用,主要用户关闭资源。无论是否发生异常,资源都必须进行关闭。System.exit(0); //退出jvm,只有这种情况finally不执行。如果父类或者接口中的方法没有抛出过异常,那么子类是不可以抛出异常的,如果子类的覆盖的方法中出现了异常,只能try不能throws。如果这个异常子类无法处理,已经影响了子类方法的具体运算,这时可以在子类方法中,通过throw抛出RuntimeException异常或者其子类,这样,子类的方法上是不需要throws声明的。
* 常见异常:脚标越界异常(IndexOutOfBoundsException)包括数组、字符串; 空指针异常(NullPointerException)、类型转换异常:ClassCastException 、没有这个元素异常:NullPointerException 、不支持操作异常;
(2)包(package)
* 包对类文件进行分类管理;给类文件提供多层名称空间。如果生成的包不在当前目录下,需要最好执行classpath,将包所在父目录定义到classpath变量中即可。一般在定义包名时,因为包的出现是为了区分重名的类。所以包名要尽量唯一,可以使用url域名来进行包名称的定义。包名的写法规范:所有字母都小写。 //package cn.itcast.pack.demo;
* 包是一种封装形式,用于封装类,想要被包以外的程序访问,该类必须public; 类中的成员,如果被包以外访问,也必须public; 包与包之间访问可以使用的权限有两种: public 、protected:只能是不同包中的子类可以使用的权限。
* Import - 导入:类名称变长,写起来很麻烦。为了简化,使用了一个关键字:import,可以使用这个关键字导入指定包中的类。实际开发时,到的哪个类就导入哪个类,不建议使用*. import packa.*; import packa.abc.*;如果导入的两个包中存在着相同名称的类。这时如果用到该类,必须在代码中指定包名。
* 常用软件包:
java.lang : java的核心包,Object System String Throwable jdk1.2版本后,该包中的类自动被导入。
java.awt : 定义的都是用于java图形界面开发的对象。
javax.swing: 提供所有的windows桌面应用程序包括的控件,比如: Frame , Dialog, Table, List 等等,就是java的图形界面库。
java.net : 用于java网络编程方面的对象都在该包中。
java.io : input output 用于操作设备上数据的对象都在该包中。比如:读取硬盘数据,往硬盘写入数据。
java.util : java的工具包,时间对象,集合框架。
java.applet: application+let 客户端java小程序。server+let --> servlet 服务端java小程序。
jar :java的压缩包,主要用于存储类文件,或者配置文件等。 命令格式: jar –cf 包名.jar 包目录 解压缩:jar –xvf 包名.jar 、将jar包目录列表重定向到一个文件中:jar –tf 包名.jar >c:\1.txt
(3)常用类--String类
* String表示字符串,所谓字符串,就是一连串的字符,不可变类,一旦String对象被创建,包含在对象中的字符序列(内容)是不可变的,直到对象被销毁;
* 常量池:JVM中一块独立的区域存放字符串常量和基本类型常量(public static final)。String使用private final char value[]来实现字符串的存储,也就是说String对象创建之后,就不能再修改此对象中存储的字符串内容,就是因为如此,才说String类型是不可变的.
* String对象比较: 单独使用”“引号创建的字符串都是常量,编译期就已经确定存储到常量池中; 使用new String(“”)创建的对象会存储到堆内存中,是运行期新创建的; 使用只包含常量的字符串连接符如”aa” + “bb”创建的也是常量,编译期就能确定,已经确定存储到常量池中; 使用包含变量的字符串连接符如”aa” + s1创建的对象是运行期才创建的,存储在堆中;
* String方法:
String():初始化一个新的 String 对象,使其表示一个空字符序列,并不是返回空(不等于 null)。
String(StringBuffer buffer):根据StringBuffer对象来创建String对象;
String(StringBuilder builder):同上
char charAt(int index):取字符串中指定位置的字符,index从0开始计算。
String concat(String str):连接字符串,等同于 “+”;
boolean contentEquals(StringBuffer buffer):若二者包含的字符序列相同时就返回true;
boolean equals(Object obj):将该字符串与指定对象比较,若二者包含的字符序列相等返回true;
boolean equalsIgnoreCase(String anotherString) :将此 String 与另一个 String 比较,不考虑大小写;
byte[] getBytes():将该字符串转换成byte数组;
int indexOf(String str):找出str字符串在该字符串中第一次出现的位置;
int indexOf(String str, int fromIndex): 返回指定子字符串在此字符串中第一次出现处的索引,从指定的索引开始;
int lastIndexOf(String str) :返回指定子字符串在此字符串中最后一次出现处的索引;
int length():返回当前字符串长度;
String replace(char oldChar, char newChar) :返回一个新的字符串,它是通过用 newChar 替换此字符串中出现的所有 oldChar 得到的。
String replaceAll(String regex, String replacement): 使用给定的 字符串replacement 替换此字符串所有的regex字符串;
boolean startsWith(String prefix): 测试此字符串是否以指定的前缀开始.
String[] split(String regex): 把字符串按指定的字符串分隔开。
String substring(int beginIndex): 返回一个新的字符串,从beginIndex开始截取,它是此字符串的一个子字符串;
String substring(int beginIndex, int endIndex): 返回一个新字符串,它是此字符串的一个子字符串;[begin,end)
char[] toCharArray(): 将此字符串转换为一个新的字符数组。
String toLowerCase() :将此 String 中的所有字符都转换为小写;
String toUpperCase():转成大写;
static String valueOf(基本类型 obj):把基本类型值转成字符串;
String trim() :忽略字符串前导空白和尾部空白。
* StringBuffer 与 StringBuilder:String是不可变类,一旦String对象被创建,包含在对象中的字符序列是不可变的,直到对象被销毁;StringBuffer 与 StringBuilder对象则是可变的!
StringBuffer: 是线程安全的;
StringBuilder: 是线程不安全的,性能高点,推荐使StringBuilder;(jdk1.5出现)
StringBuffer的字符序列是可变的(通过append等方法操作)
String toString() 返回此序列中数据的字符串表示形式。
StringBuffer(String str):以指定的字符串创建StringBuffer对象。
public StringBuilder()构造一个不带任何字符的StringBuilder对象。
StringBuffer(String str) :构造一个字符串缓冲区,并将其内容初始化为指定的字符串内容。
StringBuffer append(Object o) :将指定的任意类型对象追加到此StringBuffer 对象。
StringBuffer insert(int offset, Object o) :将任意类型参数的字符串表示形式插入此序列中。
StringBuffer delete(int start, int end) :移除此序列的子字符串中的字符。
StringBuffer deleteCharAt(int index): 移除此序列指定位置的 char。
(3)常用类--Math类、Random类、UUID类
* Math类
public final class Math extends Object
以下X表示double,float,int, long
abs(X x):求绝对值
max(X x1,X x2):求最大值
min(X x1,X x2):求最小值
public static double random():返回带正号的 double 值,该值大于等于 0.0 且小于 1.0。和使用new java.util.Random一样
Math.PI;
* Random类
负责生成伪随机数;
Random() 创建一个新的随机数生成器。
int nextInt() 返回下一个伪随机数,它是此随机数生成器的序列中均匀分布的 int 值。
int nextInt(int n) 返回一个伪随机数,它是取自此随机数生成器序列的、在 0(包括)和指定值n(不包括)之间均匀分布的 int 值。
* UUID类:用唯一标识符 (UUID) 的类。 UUID 表示一个 128 位的值。
UUID(Universally Unique Identifier)全局唯一标识符,是指在一台机器上生成的数字,它保证对在同一时空中的所有机器都是唯一的。按照开放软件基金会(OSF)制定的标准计算,用到了以太网卡地址、纳秒级时间、芯片ID码和许多可能的数字。由以下几部分的组合:当前日期和时间(UUID的第一个部分与时间有关,如果你在生成一个UUID之后,过几秒又生成一个UUID,则第一个部分不同,其余相同),时钟序列,全局唯一的IEEE机器识别号(如果有网卡,从网卡获得,没有网卡以其他方式获得),UUID的唯一缺陷在于生成的结果串会比较长。
标准的UUID格式为:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxx (8-4-4-4-12),其中每个 x 是 0-9 或 a-f 范围内的一个十六进制的数字;
UUID uuid = UUID.randomUUID();
String uid = uuid.toString();
(3)常用类--Date类、Calendar类
* Date() 分配 Date 对象并初始化此对象,以表示分配它的时间(精确到毫秒)。 Date(long date) 分配 Date 对象并初始化此对象,以表示自从标准基准时间(称为“历元(epoch)”,即 1970 年 1 月 1 日 00:00:00 GMT)以来的指定毫秒数。
SimpleDateFormat
java.text.SimpleDateFormat
SimpleDateFormat 是一个与语言环境有关的方式来格式化和解析日期的具体类。它允许进行格式化(日期 -> 文本)、解析(文本 -> 日期)和规范化。
SimpleDateFormat(String pattern) 用给定的模式和默认语言环境的日期格式符号构造 SimpleDateFormat。
public final String format(Date date)将一个 Date 格式化为日期/时间字符串。
public Date parse(String source) throws ParseException:把字符串source表示的时间按source的格式转成Date对象。
* Calendar类
推荐使用处理日期和时间的类Calendar;
是抽象类,不能实例化,通过static Calendar getInstance() 获得一个Calendar对象。int get(int field):返回指定日历字段值
静态常量:
YEAR 表示年的字段数字。
MONTH 表示月份字段数字,月份范围是[0,11]。
DATE 表示一个月中的某天。
DAY_OF_MONTH 表示一个月中的某天。
DAY_OF_WEEK 表示一个星期中的某天。
HOUR_OF_DAY / HOUR 表示第几小时
MINUTE 表示第几分钟
SECOND 表示第几秒
Date getTime() 返回一个表示此 Calendar 时间值的 Date 对象。
void set(int year, int month, int date, int hour, int minute, int second) 设置字段 YEAR、MONTH、DAY_OF_MONTH、HOUR、MINUTE 和 SECOND 的值。
abstract void add(int field, int amount) 根据日历的规则,为给定的日历字段添加或减去指定的时间量。
若 amount为负数,则减去一天,若 amount为正数 ,则加上一天;
(3)常用类--System类、Runtime类
* System 类
包含一些与系统相关的类字段和方法。它不能被实例化,类中所有属性和方法都是static,可直接被System调用。
常用方法:
static void exit(int status) 终止虚拟机的运行.对于发生了异常情况而想终止虚 拟机的运行,传递一个非0数值,对于正常情况下退出系统传递0值; 该方法实际调用的是Runtime.getRuntime().exit(int status);
static void arraycopy(Object src, int srcPos, Object dest, int destPos, int length) 数组拷贝
static long currentTimeMillis() 返回以毫秒为单位的当前时间。
String getenv(String name) 获得指定的环境变量;
static void gc() 运行垃圾回收器。
实际上调用了 Runtime中的gc()方法;
Runtime.getRuntime().exec(“notepad “);
static Properties getProperties() 取得当前的系统属性。
static String getProperty(String key) 取得指定键指示的系统属性。
static String getProperty(String key, String def) 获取用指定键描述的系统属性,def表示默认信息。
* Runtime类
类中没有构造方法,不能创建对象。但是有非静态方法,说明该类中应该定义好了对象,并可以通过一个static方法获取这个对象。用这个对象来调用非静态方法。这个方法就是 static Runtime getRuntime(); 这个Runtime其实使用单例设计模式进行设计。
(4)线程技术
* 进程和线程:进程是指一个内存中运行的应用程序,每个进程都有自己独立的一块内存空间,一个进程中可以有多个线程。比如在Windows系统中,一个运行的xx.exe就是一个进程。Java程序的进程里有几个线程:主线程, 垃圾回收线程(后台线程) 。线程是指进程中的一个执行任务(控制单元),一个进程中可以运行多个线程,多个线程可共享数据。多进程是操作系统中同时运行的多个程序;多线程:在同一个进程中同时运行的多个任务;一个进程至少有一个线程,为了提高效率,可以在一个进程中开启多个控制单元并发运行。如:多线程下载软件。 可以完成同时运行,但是通过程序运行的结果发现,虽然同时运行,但是每一次结果都不一致。 因为多线程存在一个特性:随机性。 造成的原因:CPU在瞬间不断切换去处理各个线程而导致的。 可以理解成多个线程在抢cpu资源。线程具有许多传统进程所具有的特征,故又称为轻型进程(Light—Weight Process)或进程元;而把传统的进程称为重型进程(Heavy—Weight Process),它相当于只有一个线程的任务。在引入了线程的操作系统中,通常一个进程都有若干个线程,至少需要一个线程。
* 进程与线程的区别:进程有独立的进程空间,进程中的数据存放空间(堆空间和栈空间)是独立的。线程的堆空间是共享的,栈空间是独立的,线程消耗的资源也比进程小,相互之间可以影响的。
* 创建线程方式:
a. 继承Thread类,子类覆写父类中的run方法,将线程运行的代码存放在run中,建立子类对象的同时线程也被创建,通过调用start()方法开启线程。
b. 实现Runnable接口,子类覆盖接口中的run方法,通过Thread类创建线程,并将实现了Runnable接口的子类对象作为参数传递给Thread类的构造函数。
Thread类对象调用start()方法开启线程。
A extends Thread: 简单 、不能再继承其他类了(Java单继承) 、同份资源不共享 。
A implements Runnable:(推荐) 、多个线程共享一个目标资源,适合多线程处理同一份资源。该类还可以继承其他类,也可以实现其他接口。
* 线程的生命周期:
Thread类内部有个public的枚举Thread.State,里边将线程的状态分为:
NEW——-新建状态,至今尚未启动的线程处于这种状态。
RUNNABLE——-运行状态,正在 Java 虚拟机中执行的线程处于这种状态。
BLOCKED——-阻塞状态,受阻塞并等待某个监视器锁的线程处于这种状态。
WAITING——-冻结状态,无限期地等待另一个线程来执行某一特定操作的线程处于这种状态。
TIMED_WAITING——-等待状态,等待另一个线程来执行取决于指定等待时间的操作的线程处于这种状态。
TERMINATED——-已退出的线程处于这种状态。
* 控制线程:
join方法:调用join方法的线程对象强制运行,该线程强制运行期间,其他线程无法运行,必须等到该线程结束后其他线程才可以运行。
join方法的重载方法: join(long millis);join(long millis,int nanos);
通常很少使用第三个方法: 程序无须精确到一纳秒; 计算机硬件和操作系统也无法精确到一纳秒;
* Daemon
后台线程:处于后台运行,任务是为其他线程提供服务。也称为“守护线程”或“精灵线程”。JVM的垃圾回收就是典型的后台线程。 特点:若所有的前台线程都死亡,后台线程自动死亡。设置后台线程:Thread对象setDaemon(true); setDaemon(true)必须在start()调用前。否则出现IllegalThreadStateException异常;前台线程创建的线程默认是前台线程; 判断是否是后台线程:使用Thread对象的isDaemon()方法; 并且当且仅当创建线程是后台线程时,新线程才是后台线程。
* sleep
线程休眠: 让执行的线程暂停一段时间,进入阻塞状态。
sleep(long milllis) throws InterruptedException:毫秒
sleep(long millis,int nanos)
throws InterruptedException:毫秒,纳秒
调用sleep()后,在指定时间段之内,该线程不会获得执行的机会,不会释放锁。
* 线程优先级
每个线程都有优先级,优先级的高低只和线程获得执行机会的次数多少有关。
并非线程优先级越高的就一定先执行,哪个线程的先运行取决于CPU的调度;
默认情况下main线程具有普通的优先级,而它创建的线程也具有普通优先级。
Thread对象的setPriority(int x)和getPriority()来设置和获得优先级。
MAX_PRIORITY : 值是10
MIN_PRIORITY : 值是1
NORM_PRIORITY : 值是5(主方法默认优先级)
* yield
线程礼让: 暂停当前正在执行的线程对象,释放锁并执行其他线程;
Thread的静态方法,可以是当前线程暂停,但是不会阻塞该线程,而是进入就绪状态。所以完全有可能:某个线程调用了yield()之后,线程调度器又把他调度出来重新执行。
* 多线程安全问题
导致安全问题原因: 多个线程访问出现延迟、线程随机性;可以通过Thread.sleep(long time)方法来简单模拟延迟情况。对多条操作共享数据的语句,只能让一个线程都执行完。在执行过程中,其他线程不可以参与执行。
解决的三种方法:
a. 同步代码块:
synchronized(obj)
{
//obj表示同步监视器,是同一个同步对象
/**
TODO
*/
}
b. 同步方法
格式: 在方法上加上synchronized修饰符即可。(一般不直接在run方法)
synchronized 返回值类型 方法名(参数列表)
{
/**
TODO
*/
}
c. 同步锁 (jkd1.5后的另一种同步机制): 通过显示定义同步锁对象来实现同步,这种机制,同步锁应该使用Lock对象充当。 在实现线程安全控制中,通常使用ReentrantLock(可重入锁)。使用该对象可以显示地加锁和解锁。具有与使用 synchronized 方法和语句所访问的隐式监视器锁相同的一些基本行为和语义,但功能更强大。
同步函数使用的锁是this,静态同步函数的锁是该类的字节码文件对象。
* 线程通信:
线程的不确定性,导致两种情况发生:
若生产者线程刚向存储空间添加了部分数据,CPU就切换到了消费者线程,消费者线程把部分数据和上一个数据联系到一起; 生产者放了若干数据,消费者才开始取数据,或者是消费者取完一个数据,还没等到生产者放入新的数据,又重复的取出已取过的数据;
生产者和消费者问题:
wait():让当前线程放弃监视器进入等待,直到其他线程调用同一个监视器并调用notify()或notifyAll()为止。
notify():唤醒在同一对象监听器中调用wait方法的第一个线程。
notifyAll():唤醒在同一对象监听器中调用wait方法的所有线程。
wait()、notify()、notifyAll(),这三个方法属于Object 不属于 Thread,这三个方法必须由同步监视对象来调用,两种情况:
a. synchronized修饰的方法,因为该类的默认实例(this)就是同步监视器,所以可以在同步方法中调用这三个方法;
b. synchronized修饰的同步代码块,同步监视器是括号里的对象,所以必须使用该对象调用这三个方法;
* 多线程在JDK1.5版本升级时,推出一个接口Lock接口
< java.util.concurrent.locks > Condition接口:await()、signal()、signalAll();
Lock代替了同步方法或同步代码块,Condition代替了同步监视器的功能;
Condition对象通过Lock对象的newCondition()方法创建;
方法包括:
await(): 等价于同步监听器的wait()方法;
signal(): 等价于同步监听器的notify()方法;
signalAll(): 等价于同步监听器的notifyAll()方法;
* 同步死锁:通常只要将同步进行嵌套,就可以看到现象。同步函数中有同步代码块,同步代码块中还有同步函数。
* wait和sleep区别:
a. wait:可以指定时间也可以不指定时间。不指定时间,只能由对应的notify或者notifyAll来唤醒。 sleep:必须指定时间,时间到自动从冻结状态转成运行状态(临时阻塞状态)。
b. wait:线程会释放执行权,而且线程会释放锁。 Sleep:线程会释放执行权,但不是不释放锁。
* 线程的停止:通过stop方法就可以停止线程。但是这个方式过时了。让线程运行的代码结束,也就是结束run方法。
第一种方式:定义循环的结束标记。
第二种方式:如果线程处于了冻结状态,是不可能读到标记的,这时就需要通过Thread类中的interrupt方法,将其冻结状态强制清除。让线程恢复具备执行资格的状态,让线程可以读到标记,并结束。
(5)集合框架
* 集合只用于存储对象,集合长度是可变的,集合可以存储不同类型的对象。对象封装数据,对象多了也需要存储。集合用于存储对象。对象的个数确定可以使用数组,如果不确定大小可以用集合,因为集合是可变长度的。
* 集合和数组的区别:
1:数组是固定长度的;集合可变长度的。
2:数组可以存储基本数据类型,也可以存储引用数据类型;集合只能存储引用数据类型。
3:数组存储的元素必须是同一个数据类型;集合存储的对象可以是不同数据类型。
* Collection接口
--< java.util >-- Collection接口: Collection:
|--List:有序(元素存入集合的顺序和取出的顺序一致),元素都有索引。元素可以重复。 |--Set:无序(存入和取出顺序有可能不一致),不可以存储重复元素。必须保证元素唯一性。
1:添加:
add(object):添加一个元素
addAll(Collection) :添加一个集合中的所有元素。
2:删除:
clear():将集合中的元素全删除,清空集合。
remove(obj) :删除集合中指定的对象。注意:删除成功,集合的长度会改变。 removeAll(collection) :删除部分元素。部分元素和传入Collection一致。
3:判断:
boolean contains(obj) :集合中是否包含指定元素 。
boolean containsAll(Collection) :集合中是否包含指定的多个元素。 boolean isEmpty():集合中是否有元素。
4:获取:
int size():集合中有几个元素。
5:取交集:
boolean retainAll(Collection) :对当前集合中保留和指定集合中的相同的元素。如果两个集合元素相同,返回flase;如果retainAll修改了当前集合,返回true。
6:获取集合中所有元素:
Iterator iterator():迭代器
7:将集合变成数组: toArray();
* Iterator接口
--< java.util >-- Iterator接口:
迭代器:是一个接口。作用:用于取集合中的元素。只要通过该接口就可以取出Collection集合中的元素,至于每一个具体的容器依据自己的数据结构,如何实现的具体取出细节,这个不用关心,这样就降低了取出元素和具体集合的耦合性。
boolean hasNext() 如果仍有元素可以迭代,则返回 true。
E next() 返回迭代的下一个元素。
void remove() 从迭代器指向的 collection 中移除迭代器返回的最后一个元素(可选操作)。
* List接口
--< java.util >-- List接口:
List本身是Collection接口的子接口,具备了Collection的所有方法。有序(元素存入集合的顺序和取出的顺序一致),元素都有索引。元素可以重复。
|--ArrayList:底层的数据结构是数组,线程不同步,ArrayList替代了Vector,查询元素的速度非常快。
|--LinkedList:底层的数据结构是链表,线程不同步,增删元素的速度非常快。
|--Vector:底层的数据结构就是数组,线程同步的,Vector无论查询和增删都巨慢。
1:添加:
add(index,element) :在指定的索引位插入元素。
addAll(index,collection) :在指定的索引位插入一堆元素。
2:删除:
remove(index) :删除指定索引位的元素。 返回被删的元素。
3:获取:
Object get(index) :通过索引获取指定元素。
int indexOf(obj) :获取指定元素第一次出现的索引位,如果该元素不存在返回-1;所以,通过-1,可以判断一个元素是否存在。int lastIndexOf(Object o) :反向索引指定元素的位置。List subList(start,end) :获取子列表。
4:修改:
Object set(index,element) :对指定索引位进行元素的修改。
5:获取所有元素:
ListIterator listIterator():list集合特有的迭代器。
List集合支持对元素的增、删、改、查。List集合因为角标有了自己的获取元素的方式: 遍历。 for(int x=0; x<list.size(); x++){ sop("get:"+list.get(x)); }
在进行list列表元素迭代的时候,如果想要在迭代过程中,想要对元素进行操作的时候,比如满足条件添加新元素。会发生.ConcurrentModificationException并发修改异常。导致的原因是: 集合引用和迭代器引用在同时操作元素,通过集合获取到对应的迭代器后,在迭代中,进行集合引用的元素添加,迭代器并不知道,所以会出现异常情况。既然是在迭代中对元素进行操作,找迭代器的方法最为合适.可是Iterator中只有hasNext,next,remove方法.通过查阅的它的子接口,ListIterator,发现该列表迭代器接口具备了对元素的增、删、改、查的动作。ListIterator是List集合特有的迭代器。ListIterator it = list.listIterator;//取代Iterator it = list.iterator; 方法摘要
void add(E e) 将指定的元素插入列表(可选操作)。
boolean hasNext() 以正向遍历列表时,如果列表迭代器有多个元素,则返回 true(换句话说,如果 next 返回一个元素而不是抛出异常,则返回 true)。
boolean hasPrevious() 如果以逆向遍历列表,列表迭代器有多个元素,则返回 true。
E next() 返回列表中的下一个元素。
int nextIndex() 返回对 next 的后续调用所返回元素的索引。
E previous() 返回列表中的前一个元素。
int previousIndex() 返回对 previous 的后续调用所返回元素的索引。
void remove() 从列表中移除由 next 或 previous 返回的最后一个元素(可选操作)。 void set(E e) 用指定元素替换 next 或 previous 返回的最后一个元素(可选操作)。
* 可变长度数组的原理:当元素超出数组长度,会产生一个新数组,将原数组的数据复制到新数组中,再将新的元素添加到新数组中。
ArrayList:是按照原数组的50%延长。构造一个初始容量为 10 的空列表。 Vector:是按照原数组的100%延长。对于list集合,底层判断元素是否相同,其实用的是元素自身的equals方法完成的。所以建议元素都要复写equals方法,建立元素对象自己的比较相同的条件依据。
LinkedList的特有方法:addFirst(); addLast(); 在jdk1.6以后:具有offerFirst(); offerLast();
getFirst():获取链表中的第一个元素。如果链表为空,抛出NoSuchElementException; getLast();获取链表中的最后一个元素。如果链表为空,抛出NoSuchElementException; 在jdk1.6以后。
peekFirst();获取链表中的第一个元素。如果链表为空,返回null。 peekLast();
removeFirst():获取链表中的第一个元素,但是会删除链表中的第一个元素。如果链表为空,抛出NoSuchElementException removeLast(); 在jdk1.6以后。
pollFirst();获取链表中的第一个元素,但是会删除链表中的第一个元素。如果链表为空,返回null。
pollLast();
* Set接口
--< java.util >-- Set接口: 数据结构:数据的存储方式;
Set接口中的方法和Collection中方法一致的。Set接口取出方式只有一种,迭代器。
|--HashSet:底层数据结构是哈希表,线程是不同步的。无序,高效; HashSet集合保证元素唯一性:通过元素的hashCode方法,和equals方法完成的。当元素的hashCode值相同时,才继续判断元素的equals是否为true。如果为true,那么视为相同元素,不存。如果为false,那么存储。 如果hashCode值不同,那么不判断equals,从而提高对象比较的速度。
|--LinkedHashSet:有序,hashset的子类。
|--TreeSet:对Set集合中的元素的进行指定顺序的排序。不同步。TreeSet底层的数据结构就是二叉树。
* 哈希表原理:
1:对对象元素中的关键字(对象中的特有数据),进行哈希算法的运算,并得出一个具体的算法值,这个值 称为哈希值。
2:哈希值就是这个元素的位置。
3:如果哈希值出现冲突,再次判断这个关键字对应的对象是否相同。如果对象相同,就不存储,因为元素重复。如果对象不同,就存储,在原来对象的哈希值基础 +1顺延。
4:存储哈希值的结构,我们称为哈希表。
5:既然哈希表是根据哈希值存储的,为了提高效率,最好保证对象的关键字是唯一的。 这样可以尽量少的判断关键字对应的对象是否相同,提高了哈希表的操作效率。
TreeSet: 用于对Set集合进行元素的指定顺序排序,排序需要依据元素自身具备的比较性。如果元素不具备比较性,在运行时会发生ClassCastException异常。所以需要元素实现Comparable接口,强制让元素具备比较性,复写compareTo方法。依据compareTo方法的返回值,确定元素在TreeSet数据结构中的位置。TreeSet方法保证元素唯一性的方式:就是参考比较方法的结果是否为0,如果return 0,视为两个对象重复,不存。
TreeSet集合排序有两种方式:Comparable和Comparator:
1:让元素自身具备可比较性,需要元素对象实现Comparable接口,覆盖compareTo方法。
2:让集合自身具备比较性,需要定义一个实现了Comparator接口的比较器,并覆盖compare方法,并将该类对象作为实际参数传递给TreeSet集合的构造函数。此方式较为灵活。
*Map接口
Map集合:
|--Hashtable:底层是哈希表数据结构,是线程同步的。不可以存储null键,null值。
|--HashMap:底层是哈希表数据结构,是线程不同步的。可以存储null键,null值。替代了Hashtable.
|--TreeMap:底层是二叉树结构,可以对map集合中的键进行指定顺序的排序。
Map集合存储和Collection有着很大不同:
Collection一次存一个元素;Map一次存一对元素。 Collection是单列集合;Map是双列集合。
Map中的存储的一对元素:一个是键,一个是值,键与值之间有对应(映射)关系。 特点:要保证map集合中键的唯一性。
1,添加。
put(key,value):当存储的键相同时,新的值会替换老的值,并将老值返回。如果键没有重复,返回null。
void putAll(Map);
2:删除:
void clear():清空
value remove(key) :删除指定键。
3:判断:
boolean isEmpty():
boolean containsKey(key):是否包含key
boolean containsValue(value) :是否包含value
4:取出:
int size():返回长度
value get(key) :通过指定键获取对应的值。如果返回null,可以判断该键不存在。当然有特殊情况,就是在hashmap集合中,是可以存储null键null值的。Collection values():获取map集合中的所有的值。
* 获取Map的所有元素:map中是没有迭代器的,collection具备迭代器,只要将map集合转成Set集合,可以使用迭代器了。之所以转成set,是因为map集合具备着键的唯一性,其实set集合就来自于map,set集合底层其实用的就是map的方法。
* 把map集合转成set的方法: Set keySet(); Set entrySet();//取的是键和值的映射关系。 Entry就是Map接口中的内部接口;entry是访问键值关系的入口,是map的入口,访问的是map中的键值对。
Set keySet = map.keySet();
Iterator it = keySet.iterator();
while(it.hasNext()) {
Object key = it.next();
Object value = map.get(key);
System.out.println(key+":"+value); }
--------------------------------------------------------
Set entrySet = map.entrySet();
Iterator it = entrySet.iterator(); while(it.hasNext()) {
Map.Entry me = (Map.Entry)it.next();
System.out.println(me.getKey()+"::::"+me.getValue()); }
* 集合技巧:
1:看到Array就是数组结构,有角标,查询速度很快。
2:看到link就是链表结构:增删速度快,而且有特有方法。addFirst; addLast; removeFirst(); removeLast(); getFirst();getLast();
3:看到hash就是哈希表,就要想要哈希值,就要想到唯一性,就要想到存入到该结构的中的元素必须覆盖hashCode,equals方法。
4:看到tree就是二叉树,就要想到排序,就想要用到比较。 比较的两种方式: 一个是Comparable:覆盖compareTo方法; 一个是Comparator:覆盖compare方法。
5:LinkedHashSet,LinkedHashMap:这两个集合可以保证哈希表有存入顺序和取出顺序一致,保证哈希表有序。
6:当存储的是一个元素时,就用Collection。当存储对象之间存在着映射关系时,就使用Map集合。保证唯一,就用Set。不保证唯一,就用List。
* Collections:给集合操作提供了更多的功能。这个类不需要创建对象,内部提供的都是静态方法。
* Arrays:用于操作数组对象的工具类,里面都是静态方法。asList方法:将数组转换成list集合。集合变数组:用的是Collection接口中的方法:toArray();
* 高级for循环不可以遍历map集合吗,但是可以将map转成set后再使用for-each语句。
* 集合迭代注意问题:在迭代集合的过程中,不能对集合进行增删操作(会报并发访问异常);可以用迭代器的方法进行操作(子类listIterator:有增删的方法)。增强for循环注意问题:在使用增强for循环时,不能对元素进行赋值;
* 静态导入:导入了类中的所有静态成员,简化静态成员的书写。
(6)泛型:将运行时期的问题ClassCastException问题转换成了编译失败,体现在编译时期,程序员就可以解决问题。避免了强制转换的麻烦。只要带有<>的类或者接口,都属于带有类型参数的类或者接口,在使用这些类或者接口时,必须给<>中传递一个具体的引用数据类型。
* 泛型类:当类中的操作的引用数据类型不确定的时候,以前用的Object来进行扩展的,现在可以用泛型来表示。这样可以避免强转的麻烦,而且将运行问题转移到的编译时期。
* 泛型通配符:可以解决当具体类型不确定的时候,这个通配符就是 ? ;当操作类型时,不需要使用类型的具体功能时,只使用Object类中的功能。那么可以用 ? 通配符来表未知类型。
* 泛型限定:
上限:?extends E:表示这个对象的实例,可以接收E类型或者E的子类型对象。 往集合中添加元素时,既可以添加E类型对象,又可以添加E的子类型对象。因为取的时候,E类型既可以接收E类对象,又可以接收E的子类型对象。
下限:?super E:可以接收E类型或者E的父类型对象。当从集合中获取元素进行操作的时候,可以用当前元素的类型接收,也可以用当前元素的父类型接收。
* 泛型到底代表什么类型取决于调用者传入的类型,如果没传,默认是Object类型;使用带泛型的类创建对象时,等式两边指定的泛型必须一致,因为编译器检查对象调用方法时只看变量,然而程序运行期间调用方法时就要考虑对象具体类型。
(7)IO流
* 流可以理解数据的流动,就是一个数据流。IO流最终要以对象来体现,对象都存在IO包中。主要用于处理设备上数据,在流中一般以字节的形式存放着数据。
* 流分类:输入流(读)和输出流(写)。处理的数据不同,分为字节流和字符流。io一定要写finally;
* 字节流:处理字节数据的流对象。设备上的数据无论是图片或者dvd,文字,它们都以二进制存储的。二进制的最终都是以一个8位为数据单元进行体现,所以计算机中的最小数据单元就是字节。意味着,字节流可以处理设备上的所有数据,所以字节流一样可以处理字符数据。
* 字符流:因为字符每个国家都不一样,所以涉及到了字符编码问题,那么GBK编码的中文用unicode编码解析是有问题的,所以需要获取中文字节数据的同时+ 指定的编码表才可以解析正确数据。为了方便于文字的解析,所以将字节流和编码表封装成对象,这个对象就是字符流。只要操作字符数据,优先考虑使用字符流体系。
* 流操作:读入和写出。
* 基类:字节流:InputStream、OutputStream 字符流:Reader、Writer
* close()和flush()的区别:flush():将缓冲区的数据刷到目的地中后,流可以使用。close():将缓冲区的数据刷到目的地中后,流就关闭了,该方法主要用于结束调用的底层资源。这个动作一定做。
* FileReader:使用Reader体系,读取一个文本文件中的数据。返回 -1 ,标志读到结尾。
读取数据的第二种方式:第二种方式较为高效,自定义缓冲区。
* IO中GoF设计模式之装饰设计模式
装饰设计模式解决:对一组类进行功能的增强。
包装:写一个类(包装类)对被包装对象进行包装;
1:包装类和被包装对象要实现同样的接口;
2:包装类要持有一个被包装对象;
3:包装类在实现接口时,大部分方法是靠调用被包装对象来实现的,对于需要修改的方法我们自己实现;
* 字符流体系:
Reader:用于读取字符流的抽象类。子类必须实现的方法只有 read(char[], int, int) 和 close()。
|---BufferedReader:从字符输入流中读取文本,缓冲各个字符,从而实现字符、数组和行的高效读取。 可以指定缓冲区的大小,或者可使用默认的大小。大多数情况下,默认值就足够大了。
|---LineNumberReader:跟踪行号的缓冲字符输入流。此类定义了方法 setLineNumber(int) 和 getLineNumber(),它们可分别用于设置和获取当前行号。
|---InputStreamReader:是字节流通向字符流的桥梁:它使用指定的 charset 读取字节并将其解码为字符。它使用的字符集可以由名称指定或显式给定,或者可以接受平台默认的字符集。
|---FileReader:用来读取字符文件的便捷类。此类的构造方法假定默认字符编码和默认字节缓冲区大小都是适当的。要自己指定这些值,可以先在 FileInputStream 上构造一个 InputStreamReader。
|---CharArrayReader
|---StringReader
-------------------------------------------------
Writer:写入字符流的抽象类。子类必须实现的方法仅有 write(char[], int, int)、flush() 和 close()。
|---BufferedWriter:将文本写入字符输出流,缓冲各个字符,从而提供单个字符、数组和字符串的高效写入。
|---OutputStreamWriter:是字符流通向字节流的桥梁:可使用指定的 charset 将要写入流中的字符编码成字节。它使用的字符集可以由名称指定或显式给定,否则将接受平台默认的字符集。
|---FileWriter:用来写入字符文件的便捷类。此类的构造方法假定默认字符编码和默认字节缓冲区大小都是可接受的。要自己指定这些值,可以先在 FileOutputStream 上构造一个 OutputStreamWriter。
|---PrintWriter
|---CharArrayWriter
|---StringWriter
---------------------------------
字节流:
InputStream:是表示字节输入流的所有类的超类。
|--- FileInputStream:从文件系统中的某个文件中获得输入字节。哪些文件可用取决于主机环境。FileInputStream 用于读取诸如图像数据之类的原始字节流。要读取字符流,请考虑使用 FileReader。
|--- FilterInputStream:包含其他一些输入流,它将这些流用作其基本数据源,它可以直接传输数据或提供一些额外的功能。
|--- BufferedInputStream:该类实现缓冲的输入流。
|--- Stream:
|--- ObjectInputStream
|--- PipedInputStream
-----------------------------------------------
OutputStream:此抽象类是表示输出字节流的所有类的超类。
|--- FileOutputStream:文件输出流是用于将数据写入 File 或 FileDescriptor 的输出流。
|--- FilterOutputStream:此类是过滤输出流的所有类的超类。
|--- BufferedOutputStream:该类实现缓冲的输出流。
|--- PrintStream
|--- DataOutputStream
|--- ObjectOutputStream
|--- PipedOutputStream
--------------------------------
缓冲区:
BufferedWriter(将流和缓冲区结合):是给字符输出流提高效率用的,那就意味着,缓冲区对象建立时,必须要先有流对象。明确要提高具体的流对象的效率。
* 流对象:其实很简单,就是读取和写入。但是因为功能的不同,流的体系中提供N多的对象。
* 流操作规律:
1:明确源和目的。
数据源:就是需要读取,可以使用两个体系:InputStream、Reader;
数据汇:就是需要写入,可以使用两个体系:OutputStream、Writer;
2:操作的数据是否是纯文本数据?
如果是:数据源:Reader 数据汇:Writer
如果不是:数据源:InputStream 数据汇:OutputStream
3:明确操作的数据设备。
数据源对应的设备:硬盘(File),内存(数组),键盘(System.in)
数据汇对应的设备:硬盘(File),内存(数组),控制台(System.out)。
4:需要在基本操作上附加其他功能吗?比如缓冲,如果需要就进行装饰。以提高效率。
* 转换流:字节流 + 编码表 。没有转换,没有字符流。转换流 = 字节流+编码表。 转换流的子类File = 字节流 + 默认编码表。
* File类:将文件系统中的文件和文件夹封装成了对象。提供了更多的属性和行为可以对这些文件和文件夹进行操作。这些是流对象办不到的,因为流只操作数据。
File类常见方法:
1:创建。
boolean createNewFile():在指定目录下创建文件,如果该文件已存在,则不创建。而对操作文件的输出流而言,输出流对象已建立,就会创建文件,如果文件已存在,会覆盖。除非续写。
boolean mkdir():创建此抽象路径名指定的目录。 boolean mkdirs():创建多级目录。
2:删除。
boolean delete():删除此抽象路径名表示的文件或目录。
void deleteOnExit():在虚拟机退出时删除。
注意:在删除文件夹时,必须保证这个文件夹中没有任何内容,才可以将该文件夹用delete删除。window的删除动作,是从里往外删。注意:java删除文件不走回收站。要慎用。
3:获取.
long length():获取文件大小。
String getName():返回由此抽象路径名表示的文件或目录的名称。
String getPath():将此抽象路径名转换为一个路径名字符串。
String getAbsolutePath():返回此抽象路径名的绝对路径名字符串。
String getParent():返回此抽象路径名父目录的抽象路径名,如果此路径名没有指定父目录,则返回 null。
long lastModified():返回此抽象路径名表示的文件最后一次被修改的时间。
File.pathSeparator:返回当前系统默认的路径分隔符,windows默认为 “;”。
File.Separator:返回当前系统默认的目录分隔符,windows默认为 “\”。 4:判断:
boolean exists():判断文件或者文件夹是否存在。
boolean isDirectory():测试此抽象路径名表示的文件是否是一个目录。
boolean isFile():测试此抽象路径名表示的文件是否是一个标准文件。
boolean isHidden():测试此抽象路径名指定的文件是否是一个隐藏文件。
boolean isAbsolute():测试此抽象路径名是否为绝对路径名。
5:重命名。
boolean renameTo(File dest):可以实现移动的效果。剪切+重命名。
String[] list():列出指定目录下的当前的文件和文件夹的名称。包含隐藏文件。只有封装的对象存在并且是文件夹时,这个list()方法才有效。
* 递归:当一个功能被重复使用,而每一次使用该功能时的参数不确定,都由上次的功能元素结果来确定。功能内部又用到该功能,但是传递的参数值不确定。一定要定义递归的条件,递归的次数不要过多。容易出现 StackOverflowError 栈内存溢出错误。 其实递归就是在栈内存中不断的加载同一个函数。
* Properties
Java.util.Properties:一个可以将键值进行持久化存储的对象。Map--Hashtable的子类。
Map
|--Hashtable
|--Properties:用于属性配置文件,键和值都是字符串类型。
特点:可以持久化存储数据、键值都是字符串、一般用于配置文件。
|-- load():将流中的数据加载进集合。其实就是将读取流和指定文件相关联,并读取一行数据,因为数据是规则的key=value,所以获取一行后,通过 = 对该行数据进行切割,左边就是键,右边就是值,将键、值存储到properties集合中。
|-- store():写入各个项后,刷新输出流。
|-- list():将集合的键值数据列出到指定的目的地。
* IO扩展的流对象:基本都是装饰设计模式
Java.io.outputstream.PrintStream:打印流
1:提供了更多的功能,比如打印方法。可以直接打印任意类型的数据。
2:它有一个自动刷新机制,创建该对象,指定参数,对于指定方法可以自动刷新。
3:它使用的本机默认的字符编码.
4:该流的print方法不抛出IOException。
PrintStream可以操作目的:1:File对象。2:字符串路径。3:字节输出流。
--------------------------------------------------------
PrintWriter:具备了PrintStream的特点同时,还有自身特点:
该对象的目的地有四个:1:File对象。2:字符串路径。3:字节输出流。4:字符输出流。开发时尽量使用PrintWriter。
SequenceInputStream:序列流,作用就是将多个读取流合并成一个读取流。实现数据合并。
RandomAccessFile特点:
1:该对象即可读取,又可写入。
2:该对象中的定义了一个大型的byte数组,通过定义指针来操作这个数组。
3:可以通过该对象的getFilePointer()获取指针的位置,通过seek()方法设置指针的位置。
4:该对象操作的源和目的必须是文件。
5:其实该对象内部封装了字节读取流和字节写入流。 注意:实现随机访问,最好是数据有规律。
* 管道流:管道读取流和管道写入流可以像管道一样对接上,管道读取流就可以读取管道写入流写入的数据。需要加入多线程技术,因为单线程,先执行read,会发生死锁,因为read方法是阻塞式的,没有数据的read方法会让线程等待。
* 对象的序列化:将一个具体的对象进行持久化,写入到硬盘上。静态数据不能被序列化,因为静态数据不在堆内存中,是存储在静态方法区中。用transient 关键字修饰变量可以不进行序列化。
* Serializable接口:用于启动对象的序列化功能,可以强制让指定类具备序列化功能,该接口中没有成员,这是一个标记接口。这个标记接口用于给序列化类提供UID。这个uid是依据类中的成员的数字签名进行运行获取的。如果不需要自动获取一个uid,可以在类中,手动指定一个名称为serialVersionUID id号。依据编译器的不同,或者对信息的高度敏感性。最好每一个序列化的类都进行手动显示的UID的指定。
* DataOutputStream、DataInputStream:专门用于操作基本数据类型数据的对象。
* ByteArrayInputStream:源:内存 。ByteArrayOutputStream:目的:内存。这两个流对象不涉及底层资源调用,操作的都是内存中数组,所以不需要关闭。
(8)网络编程
* 逻辑端口:用于标识进程的逻辑地址,不同进程的标识;有效端口为0~65535,其中0~1024系统使用或保留端口。
* ip对象:InetAddress
* Socket:套接字,通信的端点。就是为网络服务提供的一种机制,通信的两端都有Socket,网络通信其实就是Socket间的通信,数据在两个Socket间通过IO传输。
* UDP传输:
1:只要是网络传输,必须有socket 。
2:数据一定要封装到数据包中,数据包中包括目的地址、端口、数据等信息。 直接操作udp不可能,对于java语言应该将udp封装成对象,易于我们的使用,这个对象就是DatagramSocket. 封装了udp传输协议的socket对象。因为数据包中包含的信息较多,为了操作这些信息方便,也一样会将其封装成对象。这个数据包对象就是:DatagramPacket.通过这个对象中的方法,就可以获取到数据包中的各种信息。DatagramSocket具备发送和接受功能,在进行udp传输时,需要明确一个是发送端,一个是接收端。
udp的发送端:
1:建立udp的socket服务,创建对象时如果没有明确端口,系统会自动分配一个未被使用的端口。
2:明确要发送的具体数据。
3:将数据封装成了数据包。
4:用socket服务的send方法将数据包发送出去。
5:关闭资源。
udp的接收端:
1:创建udp的socket服务,必须要明确一个端口,作用在于,只有发送到这个端口的数据才是这个接收端可以处理的数据。
2:定义数据包,用于存储接收到数据。
3:通过socket服务的接收方法将收到的数据存储到数据包中。
4:通过数据包的方法获取数据包中的具体数据内容,比如ip、端口、数据等等。
5:关闭资源。
* TCP传输:两个端点的建立连接后会有一个传输数据的通道,这通道称为流,而且是建立在网络基础上的流,称之为socket流。该流中既有读取,也有写入。 tcp的两个端点:一个是客户端,一个是服务端。 客户端:对应的对象,Socket 。服务端:对应的对象,ServerSocket 。
TCP客户端:
1:建立tcp的socket服务,最好明确具体的地址和端口。这个对象在创建时,就已经可以对指定ip和端口进行连接(三次握手)。
2:如果连接成功,就意味着通道建立了,socket流就已经产生了。只要获取到socket流中的读取流和写入流即可,只要通过getInputStream和getOutputStream就可以获取两个流对象。
3:关闭资源。
TCP服务端:
1,创建服务端socket服务,并监听一个端口。
2,服务端为了给客户端提供服务,获取客户端的内容,可以通过accept方法获取连接过来的客户端对象。
3,可以通过获取到的socket对象中的socket流和具体的客户端进行通讯。 4,如果通讯结束,关闭资源。注意:要先关客户端,再关服务端。
* TCP和UDP的联系和用途
区别:
二者都是有用的和常用的,如果纯粹从概念上区分二者就比较费解了,我们直接从功能上进行区分,简单明了:
这两种传输协议也就是合于适配不同的业务和不同的硬件终端。
在使用中,类似于图像、声音等对可靠性要求没有那么高的业务可以用UDP,他们不需要准确存储对准确性无要求但要求速度快。
类似于文本、程序、文件等要求可靠的数据最好就用TCP,但会牺牲一些速度。
对系统资源的要求:CP较多,UDP少。
程序结构:UDP程序结构较简单,TCP复杂。
流模式与数据报模式:TCP保证数据正确性,UDP可能丢包; TCP保证数据顺序,UDP不保证
用途 :
TCP是面向连接的,有比较高的可靠性,一些要求比较高的服务一般使用这个协议,如FTP、Telnet、SMTP、HTTP、POP3等,而 UDP是面向无连接的,使用这个协议的常见服务有DNS、SNMP、QQ等。对于QQ必须另外说明一下,QQ2003以前是只使用UDP协议的,其服务器 使用8000端口,侦听是否有信息传来,客户端使用4000端口,向外发送信息(这也就不难理解在一般的显IP的QQ版本中显示好友的IP地址信息中端口 常为4000或其后续端口的原因了),即QQ程序既接受服务又提供服务,在以后的QQ版本中也支持使用TCP协议了。
UDP是一种面向无连接的通信协议,该协议使得数据传输的速度得到大幅度的提高。视频聊天语音聊天基本都是用UPD协议。
(9)反射技术
* 动态加载一个指定的类,并获取该类中的所有的内容。而且将字节码文件封装成对象,并将字节码文件中的内容都封装成对象,这样便于操作这些成员。反射技术可以对一个类进行解剖,大大的增强了程序的扩展性。
* 反射的基本步骤:
1:获得Class对象,就是获取到指定的名称的字节码文件对象。
2:实例化对象,获得类的属性、方法或构造函数。
3:访问属性、调用方法、调用构造函数创建对象。 获取这个Class对象,有三种方式:
1:通过每个对象都具备的方法getClass来获取。弊端:必须要创建该类对象,才可以调用getClass方法。
2:每一个数据类型(基本数据类型和引用数据类型)都有一个静态的属性class。弊端:必须要先明确该类。
前两种方式不利于程序的扩展,因为都需要在程序使用具体的类来完成。
3:使用的Class类中的方法,静态的forName方法。指定什么类名,就获取什么类字节码文件对象,这种方式的扩展性最强,只要将类名的字符串传入即可。
* 反射用法:
* 创建对象的两种方式(其实就是对象在进行实例化时的初始化方式):
1,调用空参数的构造函数:使用了Class类中的newInstance()方法。
2,调用带参数的构造函数:先要获取指定参数列表的构造函数对象,然后通过该构造函数的对象的newInstance(实际参数) 进行对象的初始化。
综上所述,第二种方式,必须要先明确具体的构造函数的参数类型,不便于扩展。所以一般情况下,被反射的类,内部通常都会提供一个公有的空参数的构造函数。
(10)正则表达式
* 正则的出现,对字符串的复杂操作变得更为简单。将对字符串操作的代码用一些符号来表示。只要使用了指定符号,就可以调用底层的代码对字符串进行操作。符号的出现,简化了代码的书写。符号的出现虽然简化了书写,但是却降低了阅读性。 其实更多是用正则解决字符串操作的问题。
* 常见操作:
1:匹配:其实用的就是String类中的matches方法。 String reg = "[1-9][0-9]{4,14}"; boolean b = qq.matches(reg);//将正则和字符串关联对字符串进行匹配。
2:切割:其实用的就是String类中的split方法。
3:替换:其实用的就是String类中的replaceAll();
4:获取
a. 先要将正则表达式编译成正则对象。使用的是Pattern中静态方法 compile(regex);
b. 通过Pattern对象获取Matcher对象。Pattern用于描述正则表达式,可以对正则表达式进行解析。而将规则操作字符串,需要从新封装到匹配器对象Matcher中。然后使用Matcher对象的方法来操作字符串。通过Pattern对象中的matcher方法。该方法可以正则规则和字符串想关联。并返回匹配器对象。
c. 使用Matcher对象中的方法即可对字符串进行各种正则操作。
(11)对象清理
* Java自动垃圾回收:Java一大特性,方便编程,以消耗性能为代价。垃圾是指无用的对象,C++是需要程序员自己写析构函数来释放内存的,有可能忘记释放导致内存泄漏。Java语言对内存的分配管理是通过JVM内部机制决定的,程序员可以不关心其处理。
* 垃圾回收原理:java虚拟机中有个称为垃圾回收器的东西。垃圾回收器的作用视察找好人回收无用的对象,以便让JVM更有效的使用内存。垃圾回收器的运行时间是不确定的,由JVM决定,在运行时是间歇执行的。虽然可以通过System.gc()来强制回收垃圾,但是这个命令下达后无法保证JVM会立即执行,但会在短期内执行你的请求。JVM会在内存紧缺时去执行垃圾回收执行。垃圾回收过于频繁会导致性能下降,过于稀疏会导致内存紧缺。JVM会将其控制到最好。但是有时候短期吃掉大量内存,此时有必要强制下达一条垃圾回收命令,以便有更多的可用物理内存。没有用的对象是垃圾,当没有任何线程访问一个对象时,该对象就符合垃圾回收的条件。
* 对于String,存在一个字符串常量池,字符串池中的垃圾回收、算法和这里所讨论的垃圾回收是两码事。字符串的胡乱拼接往往会导致性能急剧下降,尤其在庞大的循环语句中,拼接字符串就是让程序员慢性自杀。字符串既然是池,就是为了缓冲,为了更高的命中率,因此垃圾回收的频率也许会比JVM对象回收器低很多。垃圾回收器仅仅能做的是尽可能保证可用内存的使用效率,让可用内存得到高效的管理。程序员可以影响垃圾回收的执行,但不能控制。
* 将无用的对象赋值为null、重新为引用变量赋值、让相互联系的对象成为“岛”对象。