当前位置:   article > 正文

javaSE 笔记全!

javaSE 笔记全!

Java基础


基本操作

dos(Disk Operating System)命令行的简单操作

dir(directory):列出当前目录下的文件及文件夹

md(make directory):创建目录

rd(remove directory):删除目录

cd(Change directory):进入指定目录

cd.. :退回到上一级目录

cd\ :退回到根目录

del(delete):删除文件

exit:退出dos命令行

echo: 创建文件 eg.  echo abc > 1.java 在当前目录下创建包含abc内容的1.java

edit:编辑文件


java 语言概述


SUN(Stanford University Network,斯坦福大学网络公司)1995年推出的一门高级编          

程语言。

是一种面向Internet的编程语言。

随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。

是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。


Java语言的三种技术架构

J2EE(Java 2 Platform Enterprise Edition)企业版

是为开发企业环境下的应用程序提供的一套解决方案。

该技术体系中包含的技术如Servlet Jsp等,主要针对于Web应用程序开发。

J2SEJava 2 Platform Standard Edition)标准版

是为开发普通桌面和商务应用程序提供的解决方案。

该技术体系是其他两者的基础,可以完成一些桌面应用程序的开发。

比如Java版的扫雷。

J2ME(Java 2 Platform Micro Edition)小型版

是为开发电子消费产品和嵌入式设备提供的解决方案。

该技术体系主要应用于小型电子消费类产品,如手机中的应用程序等。

Java5.0版本后,更名为JAVAEE JAVASE JAVAME


Java语言的特点:跨平台性


什么是跨平台性?

通过Java语言编写的应用程序在不同的系统平台上都可以运行。

原理是什么?

只要在需要运行java应用程序的操作系统上,先安装一Java虚拟机(JVM Java Virtual Machine)即可。

JVM来负责Java程序在该系统中的运行。


Java语言的特点:跨平台性(图解)


因为有了JVM,所以同一个Java程序在三个不同的操作系统中都可以执行。这样就实现了Java程序的跨平台性。也称为Java具有良好的可移植性。

什么是JRE,JDK?

JRE(Java Runtime Environment Java运行环境)

包括Java虚拟机(JVM Java Virtual Machine)Java程序所需的核心类库等,如果想要运行一个开发好的Java程序,计算机中只需要安装JRE 即可。

JDK(Java Development Kit Java开发工具包)

JDK是提供给Java开发人员使用的,其中包含了java的开发工具,也包括了JRE。所以安装了JDK,就不用在单独安装JRE了。

其中的开发工具:编译工具(javac.exe) 打包工具(jar.exe)

简单而言:使用JDK开发完成的java程序,交给JRE去运行。


Java6.0 Platform


安装JDK


傻瓜式安装,下一步即可。

建议:安装路径不要有中文或者特殊符号 如空格等。

当提示安装JRE时,可以选择不安装。


配置环境变量path

path(1)


dos命令行中敲入javac,出现错误提示。



错误原理:当前执行的程序在当前目录下如果不存在,windows系统会在系统中已有的一个名为path的环境变量指定的目录中查找。如果还没有找到,就出现以上的错误提示。

所以进入到 jdk安装路径\bin目录下,执行javac。看到javac参数提示信息。


path(2)


每次执行java的工具都要进入到bin目录下, 是非常麻烦的。


可不可以在任何目录下都可以执行java的工具呢?

根据windows系统在查找可执行程序的原理, 可以java工具所在路径定义到path环境变量中,让系统帮我们去找运行执行的程序。


path(3)


我的电脑--属性--高级--环境变量

编辑path环境变量,在变量值开始处加上java工具所在目录,后面用;和其他值分隔开即可。

重新打开DOS命令行,任意目录下敲入javac

如果出现javac 的参数信息,配置成功。

这种配置方式,一劳永逸。


path(4)


临时配置方式:通过dos命令中set命令完成 :用于查看本机的所有环境变量的信息。

set 变量名:查看具体一个环境变量的值。

set 变量名=:清空一个环境变量的值。

set 变量名=具体值:给指定变量定义具体值。


想要在原有环境变量值基础上添加新值呢?

首先,通过%变量名%操作符获取到原有环境变量的值。

然后加上新值后在定义给该变量名即可

举例:给path环境变量加入新值set path=新值;%path%


注意:这种配置方式只在当前dos窗口有效。窗口关闭,配置消失


配置技巧


为了不因为jdk的目录或者目录名称的改变,而不断更改path的值,而导致对path变量值的误操作,可以通过以下技巧完成。新创建一个环境变量JAVA_HOME 记录住jdk的目录。


path中通过%%动态的获取JAVA_HOME的值即可。

JAVA_HOME=F:\jdk1.6.0_01

path=%JAVA_HOME%\bin;%path%

%path%:动态获取path环境变量的值。

%JAVA_HOME%:动态获取名称为JAVA_HOME环境变量的值。


java程序开发体验

Hello World

Java代码编写到扩展名为.java的文件中。

通过javac命令对该java文件进行编译

通过java命令对生成的class文件进行运行



步骤


步骤一:编写选择最简单的编辑器记事本。

敲入代码

class Demo

{}

将文件保存成Demo.java,这个文件是存放java代码的文件,称为源文件


编译


步骤二:编译有了java源文件,将其编译成JVM可以识别的文件

在该源文件目录下,通过javac编译工具对Demo.java文件进行编译。

如果程序没有错误,没有任何提示,但在当前目录下会出现一个Demo.class文件,该文件称为字节码文件,也是可以执行的java的程序。


运行


步骤三:运行

有了可执行的java程序(Demo.class字节码文件)

通过运行工具java.exe对字节码文件进行执行。

出现提示:缺少一个名称为main的方法。


因为一个程序的执行需要一个起始点或者入口,所以在Demo类中的加入public static void main(String[] args){}

对修改后的Demo.java源文件需要重新编译,生成新的class文件后,在进行执行。

发现没有编译失败,但也没有任何效果,因为并没有告诉JVM要帮我们做什么事情,也就是没有可以具体执行的语句。

想要和JVM来个互动,只要在main方法中加入一句

System.out.println("hello java");在重新编译,运行即可.


配置classpath

classpath1


既然class文件(字节码文件)java的运行文件,可不可以实现, 在任意目录下都可以执行某一目录下的class文件呢?

根据path环境变量的原理,可以定义一个名为classpath环境变量,将要运行的class文件所在目录定义在该变量中。

例:set classpath=c:\

classpath变量值是java类文件的目录

path变量是windows程序文件的目录


classpath(2)


JVM查找类文件的顺序:

如果没有配置classpath环境变量,JVM只在当前目录下查找要运行的类文件。

如果配置了classpath环境,JVM先在classpath环境变量值的目录中查找要运行的类文件。

值的结尾处如果加上分号,那么JVMclasspath目录下没有找到要指定的类文件,会在当前目录下在查找一次。

值的结尾出如果没有分号,那么JVMclasspath目录下没有找到要指定的类文件,不会在当前目录下查找,即使当前目 录下有,也不会运行。


建议:配置classpath环境变量时,值的结尾处不要加分号,如果需要访问当前目录可以“.”表示


复习建议


JDK,JRE,JVM的特点。

环境变量的配置pathclasspath以及作用。

Java程序的编写,编译,运行步骤。

在配置,编写,编译,运行各个步骤中常 见的错误以及解决方法。


作业


简述JDK,JRE的区别。

简述pathclasspath环境变量的作用。

独立编写Hello World程序。

class的作用

main函数的作用

输出语句的作用


总结:



Java语言基础

Java关键字

关键字的定义和特点

定义:被Java语言赋予了特殊含义的单词

特点:关键字中所有字母都为小写

用于定义数据类型的关键字

class

interface

byte

short

int

long 

float

double

char

boolean

void





用于定义数据类型值的关键字

true

false

null



用于定义流程控制的关键字

if

else

switch

case

default

while

do

for

break

continue

return






用于定义访问权限修饰符的关键字

private

protected

public



用于定义类,函数,变量修饰符的关键字

abstract

final

static

synchronized


用于定义类与类之间关系的关键字

extends

implements




用于定义建立实例及引用实例,判断实例的关键字

new

this

super

instanceof


用于异常处理的关键字

try

catch

finally

throw

throws

用于的关键字

package

import




其他修饰符关键字

native

strictfp

transient

volatile

assert


Java标识符


在程序中自定义的一些名称。

26个英文字母大小写,数字:0-9 符号_ $ 组成

定义合法标识符规则:

数字不可以开头。

不可以使用关键字

Java中严格区分大小写

注意:在起名字的时,为了提高阅读性,要尽量有意义。


Java中的名称规范

包名:多单词组成时所有字母都小写xxxyyyzzz

类名接口名:多单词组成时,所有单词的首字母大写XxxYyyZzz

变量名和函数名:多单词组成时,第一个单词首字母小写,第二个单词开始每个单词首字母大写xxxYyyZzz

常量名:所有字母都大写。多单词时每个单词用下划线连接。XXX_YYY_ZZZ


注释


用于注解说明解释程序的文字就是注释。

提高了代码的阅读性。

Java中的注释格式:

单行注释格式://注释文字

多行注释格式:/* 注释文字*/

文档注释格式:/** 注释文字*/


对于单行和多行注释,被注释的文字,不会被JVM java虚拟机)解释执行

对于文档注释,是java特有的注释,其中注释内容可以JDK提供的工具javadoc 所解析,生成一套以网页文件形式体现的该程序的说明文档。

注释是一个程序员必须要具有的良好编程习惯。

初学者编写程序可以养成习惯:先写注释再写代码。

将自己的思想通过注释先整理出来,再用代码去体现

因为代码仅仅是思想的一种体现形式而已。


常量与变量

常量

常量表示不能改变的数值。

Java中常量的分类:


整型

整数,4个字节

长整型

整数8个字节。以L结尾。

单精度浮点数

小数,4个字节。以F结尾。

双精度浮点数

小数,8个字节

布尔

只有两个值,真(true)或假(false),1个字节。

字符

单个字符,2个字节。例如:'a', '', '5', '\u0026' , '\u0027'


在字符常量中,反斜杠(\是一个特殊的字符,它的作用是用来转义后面一个字符,这些字符通常是不可见的或者有特殊意义的。

'\r' 回车,回到一行的开始

'\n' 换行,换到下一行

'\t' 制表符,键盘上的Tab

'\b' 类似退格,键盘上的Backspace

以上字符都不可见,无法直接表示,所以用斜杠加上另外一个字符来表示。


'\'' 单引号,Java代码中单引号表示字符的开始和结束,如果直接写程序会认为前两个是一对,报错。

'\"' 双引号,Java代码中双引号表示字符串的开始和结尾,如果要写一个包含双引号的字符串那么这个双引号也需要转义。

'\\' 斜杠,Java代码中的斜杠是转义字符,用来和后面一个字符配合使用,在真正需要用斜杠的时候那么就要用另一个斜杠来转义。

以上字符都有特殊意义,无法直接表示,所以用斜杠加上另外一个字符来表示。

字符串

由若干个字符组成的一串。可以是一个字符、多个字符、或者一个都没有。字符串没有固定大小。

null只有这一个值,用来表示一个引用为空。



进制转换

对于整数:java有三种表现形式。

十进制:0-9 ,满101.

八进制:0-7 ,满81. 0开头表示

十六进制:0-9A-F,满161. 0x开头表示


进制的基本转换十进制二进制互转十进制转成二进制除以2取余数

二进制转成十进制乘以2的幂数


十进制八进制互转

十进制十六进制互转


负数的二进制表现形式对应的正数二进制取反加1





变量


变量的概念

内存中的一个存储区域

该区域有自己的名称(变量名)和类型(数据类型)

该区域的数据可以在同一类型范围内不断变化

为什么要定义变量:

用来不断的存放同一类型的常量,并可以重复使用

使用变量注意:

变量的作用范围(一对{}之间有效)

初始化值

定义变量的格式

数据类型 变量名 = 初始化值;

注:格式是固定的,记住格式,以不变应万变。

理解:变量就如同数学中的未知数。


基本数据类型

Java语言是强类型语言,对于每一种数据都定义了明确的具体数据类型,在内存总分配了不同大小的内存空间


整数默认:int 小数默认:double

a.基本数据类型: 8

整数

byte 1个字节,最小值:-128,最大值:127

short 2个字节,最小值:-32768,最大值:32767

int 4个字节,最小值:-2147483648,最大值:2147483647

long 8个字节,最小值:- 9223372036854775808,最大值:9223372036854775807

浮点数:

float 4个字节,最小值:1.4E-45最大值:3.4028235E38

double 8个字节,最小值:4.9E-324,最大值:1.7976931348623157E308

字符:

char 2个字节,最小值:0,最大值:65535

布尔:

boolean 1个字节,truefalse


b引用数据类型

类、接口、数组都是引用数据类型,除了8种基本数据类型,其他所有类型都是引用数据类型。



类型转换


自动类型转换(也叫隐式类型转换)

强制类型转换(也叫显式类型转换)

类型转换的原理

什么时候要用强制类型转换?

表达式的数据类型自动提升

所有的byte型、short型和char的值将被提升到int型。

如果一个操作数是long型,计算结果就是long型;

如果一个操作数是float型,计算结果就是float型;

如果一个操作数是double型,计算结果就是double型。

分析

System.out.println(‘a’)System.out.println(’a’+1) 的区别。


自动类型提升

byte b = 3;

int x = 4;

x = x + b;//b会自动提升为int类型进行运算。

强制类型转换

byte b = 3;

b = b + 4;//报错

b = (byte)b+4;//强制类型转换,强制将b+4的结果转换为byte类型,再赋值给b

思考:

byte b1=3,b2=4,b;

b=b1+b2;//报错

b=3+4;//编译通过,

b = 3 + 127//编译失败,130 大于byte类型的最大值

哪句是编译失败的呢?为什么呢?

2.类型转换?

/**

类型转换

*/

class VarDemo 

{

public static void main(String[] args) 

{

char c = 'a';

//c = 'a' + 'b';//编译通过,结果:?

//c = c + 'b'; //编译失败,c int类型损失精度

c += 'b';//编译通过,结果:?

System.out.println(c);

}

}


运算符

算术运算符



算术运算符的注意问题

如果对负数取模,可以把模数负号忽略不记,如:5%-2=1。但被模数是负数就另当别论。(取模运算符号看被模数,即左边

对于除号“/”,它的整数除和小数除是有区别的:整数之间做除法时,只保留整数部分而舍弃小数部分。

例如:int x=3510;x=x/1000*1000; x的结果是?3000

“+”除字符串相加功能外,还能把非字符串转换成字符串,

例如:System.out.println("5+5="+5+5);//打印结果是?5+5 = 55


习题:

aSystem.out.println(3500 / 1000 * 1000); 3000

b.某个培训中心要为新到的学员安排房间,假设共有x个学员,每个房间可以住6人,让你用一个公式来计算他们要住的房间数?


 赋值运算符

 SHAPE  \* MERGEFORMAT 


示例:

int a,b,c; a=b=c =3;

int a = 3; a+=5;等同运算a=a+5;


思考:

short s = 3; 

s=s+2;//编译报错

s+=2;//编译通过

有什么区别?


 比较运算符



比较运算符运行结束之后返回的都是boolean

注意运算符= =不要写成=


逻辑运算符

 SHAPE  \* MERGEFORMAT 


逻辑运算符用于连接布尔型表达式,在Java中不可以写成3<x<6,应该写成x>3 & x<6

“&”“&&”的区别:

&,左边无论真假,右边都进行运算

&,如果左边为真,右边参与运算,如果左边为假,那么右边不参与运算。

“|”“||”的区别同理,双或时,左边为真,右边不参与运算。

异或( ^ )与或( | )的不同之处是:当左右都为true时, 结果为false


位运算符


位运算符

运算符

运算

范例

<<

左移

3 << 2 = 12 ‐‐> 3*2*2=12

>>

右移

3 >> 1 = 1 ‐‐> 3/2=1

>>>

无符号右移

3 >>> 1 = 1 ‐‐> 3/2=1

&

与运算

6 & 3 = 2

|

或运算

6 | 3 = 7

^

异或运算

6 ^ 3 = 5

~

反码

~6 = ‐7


位运算符的细节

<<

空位补0,被移除的高位丢弃,空缺位补0

>>

被移位的二进制最高位是0,右移后,空缺位补0

最高位是1,空缺位补1

>>>

被移位二进制最高位无论是0或者是1,空缺位都用0补。

&

二进制位进行&运算,只有1&1时结果是1,否则是0;

|

二进制位进行| 运算,只有0 | 0时结果是0,否则是1;

^

任何相同二进制位进行^ 运算,结果是01^1=0 , 0^0=0

不相同二进制位^ 运算结果是11^0=1 , 0^1=1


我们可以对数据按二进制位进行移位操作,java的移位运算符有三种


<< 左移 将二进制的每一位向左移,低位补0。左移几位就相当于乘以2的几次方。

>> 右移 将二进制的每一位向右移,原来高位是0就补0,原来高位是1就补1。右移几位就相当于除以2的几次方。

>>> 无无符号右移 将二进制的每一位向右移,高位补0正数移动没区别,负数移动后变为正数。


练习:

&>>来做十进制转十六进制

有两个int型变量ab,在不使用第三个变量的情况下交换两个变量中的值

/**

不借用第三方完成变量值互换

*/

class SwapTest 

{

public static void main(String[] args) 

{

int a,b;

a = 3;

b = 4;

System.out.println("交换前:a="+a+"b="+b);

swapNum(a,b);

}


/**

不借用第三方完成变量值互换

@param a 带交换数字

@param b 带交换数字

*/

public static void swapNum(int a, int b)

{

a = a^b;

b = a^b;

a = a^b;

System.out.println("交换后:a="+a+"b="+b);

}


}

最有效率的方式算出2乘以8等于几?2<<3;

/**

高效完成2*8的运算

*/

class OperationTest 

{

public static void main(String[] args) 

{

System.out.println(2<<3);

}

}


运算符优先级

 SHAPE  \* MERGEFORMAT 

程序流程控制

 程序顺序结构

顾名思义,就是程序从上到下一行一行执行的结构,中间没有判断和跳转,直到程序结束。

 选择结构

程序具备多个分支,通过条件判断决定程序选择那一条分支执行


if语句:

通过if...else if...else决定程序流程。

如果if中的条件满足则执行其中语句,if未满足则继续判断else if,如果满足则执行,不满足继续判断下一个else if,如果所有都不满足,则执行else

练习:

if else语句判断一个数是奇数还是偶数。

用户输入一个字符,用程序判断是否为小写字母,如果是,请输出您输入的字符是小写字母 


switch语句:

通过switch...case...default语句控制程序流程。

根据switch后括号中的值判断运行哪一个case,这个值可以是byteshortcharint

注:java1.5 增加 enum 

Java 7 增加 String

default语句是可选的,如果所有case都不满足,则会执行default。(如果default语句放在结构最开始,注意加break

一旦匹配到一个case,程序就会从这个case向下执行,执行完一个case之后不会跳过其他的case,如需跳过请使用break


三元运算符

语法:表达式 ? 结果1 : 结果

如果表达式结尾为true取结果1,为false则取结果2

注意三元运算符也是有短路的效果,根据表达式的结果,只运行冒号一边的,另外一边的不参与运行。


注:

Ifswitch语句很像,如何选择使用呢?

如果判断具体数值不多,而且符合byte,short ,char,int 这四种类型

虽然两种语句都可以使用,但是建议使用switch语句效率高

其他情况:对区间判断,对结果类型为boolean进行判断,使用if,if更广。

练习

定义一个函数,接收两个int参数,返回较大的一个。

循环结构

通过循环语句让同一段代码反复执行多次,执行完毕程序才会继续往后运行


while

先判断while中的表达式结果是否为truetrue则执行循环体,执行结束之后再次判断,如果表达式结果为false则跳出循环。

练习:

打印出0-9

打印出a-z


do...while

先执行一次循环体,然后判断while中的表达式,如果是true继续执行,如果是false则跳出循环。

练习:

编写一个程序,这个程序不断地读取键盘上输入的字符,直到读到字符’q’时,程序结束。


for

for循环的括号中有三条语句,都是可选项。

语句1:这条语句会在整个循环开始之前执行,且仅运行一次,不参与循环。

语句2:必须是一个返回boolean值的表达式,如果写了这个语句那么每次循环开始之前会判断,true则执行循环,false则不执行。没写则直接执行。

语句3:这条语句在每次循环体运行结束之后执行。


注:

变量有自己的作用域,对于for来讲,如果将用于控制循环的增量定义在for语句中,那么该变量只在for语句内有效,for语句执行完毕,该变量在内存中被释放。

forwhile可以进行互换,如果需要定义循环增量,用for 更为合适。

for里面的连个表达式运行的顺序,初始化表达式只读一次,判断循环条件, 为真就执行循环体,然后再执行循环后的操作表达式,接着继续判断循环条件, 重复找个过程,直到条件不满足为止。

最简单无限循环格式:while(true) , for(;;),无限循环存在的原因是并不知道循环多少次,而是根据某些条件,来控制循环。


总结:

什么时候使用循环结构?

当要对某些语句执行很多次时,就使用循环结构。


练习:

使用星号打印如下图案


*****

*****

*****

*****

*****


i *

* 0 1

** 1 2

*** 2 3

**** 3 4

***** 4 5


i 空格 *

    * 0 4 1

   *** 1 3 3

  ***** 2 2 5

******* 3 1 7

********* 4 0 9


continuebreakreturn

continue:跳过一次循环,继续执行下一次

break:结束循环

return结束方法

注:

a,这两个语句离开应用范围,存在是没有意义的。

b,这个两个语句单独存在下面都不可以有语句,因为执行不到。

c,continue语句是结束本次循环继续下次循环。

d,标号的出现,可以让这两个语句作用于指定的范围

语句练习


语句的嵌套应用

累加求和,计数器

循环嵌套

函数

 函数的定义

什么是函数?

函数就是定义在类中的具有特定功能的一段独立小程序。

函数也称为方法。

函数的格式:

修饰符 返回值类型 函数名(参数类型形式参数1,参数类型形式参数2)

{

执行语句;

return 返回值;

}

返回值类型:函数运行后的结果的数据类型。

参数类型:是形式参数的数据类型。

形式参数:是一个变量,用于存储调用函数时传递给函数的实际参数。

实际参数:传递给形式参数的具体数值。

return:用于结束函数。

返回值:该值会返回给调用者。

 函数的特点


定义函数可以将功能代码进行封装

便于对该功能进行复用

函数只有被调用才会被执行

函数的出现提高了代码的复用性

对于函数没有具体返回值的情况,返回值类型用关键字void表示,那么该函数中的return语句如果在最后一行可以省略不写。

注意:

函数中只能调用函数,不可以在函数内部定义函数。

定义函数时,函数的结果应该返回给调用者,交由调用者处理。

函数的应用


两个明确

明确要定义的功能最后的结果是什么?

明确在定义该功能的过程中,是否需要未知内容参与运算

示例:

需求:定义一个功能,可以实现两个整数的加法运算。

分析:

该功能的运算结果是什么?两个数的和,也是一个整数(int)

在实现该功能的过程中是否有未知内容参与运算?加数和被加数是不确定的。(两个参数intint)

代码:

int getSum(int x,int y)

{

return x+y;

}

函数的重载(overload

重载的概念

在同一个类中,允许存在一个以上的同名函数,只要它们的参数个数或者参数类型不同即可。

重载的特点:

与返回值类型无关,只看参数列表。

重载的好处:

方便于阅读,优化了程序设计。

重载示例:

//返回两个整数的和

int add(int x,int y){return x+y;}

//返回三个整数的和

int add(int x,int y,int z){return x+y+z;}

//返回两个小数的和

double add(double x,double y){return x+y;}


数组

数组的定义

概念

同一种类型数据的集合。其实数组就是一个容器。

数组的好处

可以自动给数组中的元素从0开始编号,方便操作这些元素。

格式1

元素类型[] 数组名= new 元素类型[元素个数或数组长度];

示例:int[] arr = new int[5];

格式2

元素类型[] 数组名= new 元素类型[]{元素,元素,……};

int[] arr = new int[]{3,5,1,7};

int[] arr = {3,5,1,7};

数组的内存结构


内存结构

Java程序在运行时,需要在内存中的分配空间。为了提高运算效率,有对空间进行了不同区域的划分,因为每一片区域都有特定的处理数据方式和内存管理方式。

栈内存

用于存储局部变量,当数据使用完,所占空间会自动释放。

堆内存

数组和对象,通过new建立的实例都存放在堆内存中。

每一个实体都有内存地址值

实体中的变量都有默认初始化值

实体不再被使用,会在不确定的时间内被垃圾回收器回收

方法区,本地方法区,寄存器

数组常见问题

数组脚标越界异常(ArrayIndexOutOfBoundsException)

int[] arr = new int[2];

System.out.println(arr[3]);

访问到了数组中的不存在的脚标时发生。

空指针异常(NullPointerException)

int[] arr = null;

System.out.println(arr[0]);

arr引用没有指向实体,却在操作实体中的元素时。

数组常见操作


获取最值(最大值,最小值)

排序(选择排序,冒泡排序)

折半查找(二分查找)


数组中的数组

二维数组[][]

格式1int[][] arr = new int[3][2];

定义了名称为arr的二维数组

二维数组中有3个一维数组

每一个一维数组中有2个元素

一维数组的名称分别为arr[0], arr[1], arr[2]

给第一个一维数组1脚标位赋值为78写法是:arr[0][1] = 78;

格式2int[][] arr = new int[3][];

二维数组中有3个一维数组

每个一维数组都是默认初始化值null

可以对这个三个一维数组分别进行初始化

arr[0] = new int[3];

arr[1] = new int[1];

arr[2] = new int[2];

格式3int[][] arr = {{3,8,2},{2,7},{9,0,1,6}};

定义一个名称为arr的二维数组

二维数组中的有三个一维数组

每一个一维数组中具体元素也都已初始化

第一个一维数组arr[0] = {3,8,2};

第二个一维数组arr[1] = {2,7};

第三个一维数组arr[2] = {9,0,1,6};

第三个一维数组的长度表示方式:arr[2].length;

练习:获取arr数组中所有元素的和。使用for的嵌套循环即可。

注意特殊写法情况:int[] x,y[]; x是一维数组,y是二维数组。

练习


基础练习题

进制转换

幸运儿

定义一个函数,将数组中所有元素打印。要求打印成一行,每个元素之间以逗号分隔。

定义一个函数,交换数组中的两个元素。

定义一个函数,找出数组中的最大数。

定义一个函数,将数组中所有元素反转。例如:{1, 2, 3} 反转后为 {3, 2, 1}

定义一个函数,对数组进行排序。

编写一个程序,程序接收键盘上输入的三个数,并输出这三个数的最大数。

编写一个程序,它先将键盘上输入的一个字符串转换成十进制整数,然后打印出这个十进制整数对应的二进制形式。 

使用移位方式将一个十进制数转换为十六进制。三种方式:

0-9之间的数值直接加上字符'0'9以上的数值减去10以后再加上字符'A'
定义一个数组,其中包含0-F这些字符,然后用要计算的数值作为数组的索引号,即可获得其对应的十六进制数据。
Character.forDigit
静态方法可以将一个十六进制的数字转变成其对应的字符表示形式,例如,根据数值15返回字符'F'

 

面向对象

面向对象的概念

什么是面向对象 

面向对象(Object Oriented)是一种思想,90年代以后软件开发的主流思想。

由于现实社会是由各种各样的事物所组成的,而我们编程又是在模拟现实社会,那么在程序中也要用一些东西来表示现实社会中的事物,这些东西就是程序中的对象。我们在程序中使用这些对象,对其特征和行为进行操作进行编程,这就是面向对象编程。

在使用面向对象编程思想之前,我们通常用面向过程的思想编程,先分析出解决问题的步骤,然后按照步骤一步一步实现。


理解面向对象


面向对象是相对面向过程而言

面向对象和面向过程都是一种思想

面向过程

强调的是功能行为

面向对象

将功能封装进对象,强调具备了功能的对象。

面向对象是基于面向过程的。


面向对象的特点


是一种符合人们思考习惯的思想

可以将复杂的事情简单化

将程序员从执行者转换成了指挥者

完成需求时:

先要去找具有所需的功能的对象来用。

如果该对象不存在,那么创建一个具有所需功能的对象。

这样简化开发并提高复用。

向对象开发设计,设计,特征


开发的过程:其实就是不断的创建对象,使用对象, 指挥对象做事情。

设计的过程:其实就是在管理和维护对象之间的关系。

面向对象的特征:

封装(encapsulation)

继承(inheritance)

多态(polymorphism)

面向对象编程的优点

提高代码复用性。

使用者无需关心具体细节。  

转变程序员角色,更加符合人的思维习惯。


类与对象

什么是类

类是用来描述对象的。由于对象是虚拟出来的东西,是看不见摸不着的,我们需要在程序中使用对象,就需要用一种方式来描述对象,然后根据这个描述来创建对象。


使用计算机语言就是不断的在描述现实生活中的事物。

java中描述事物通过类的形式体现,类是具体事物的抽象,概念上的定义。

对象即是该类事物实实在在存在的个体。

类与对象的关系

对象是类的实例,类是对象的抽象。


类与对象的关系如图


可以理解为: 

类就是图纸 

汽车就是堆内存中的对象

类的定义


生活中描述事物无非就是描述事物的属性和行为。

如:人有身高,体重等属性,有说话,打球等行为。

Java中用class来描述事物

属性:对应类中的成员变量。

行为:对应类中的成员函数。

定义类其实在定义类中的成员(成员变量和成员函数)

class Person {

String name;

int age;

void speak(){

System.out.println("My name is " + name);

System.out.println("I am " + age + " years of age");

}

}

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


成员变量

成员变量定义在类中,在整个类中都可以被访问。

成员变量随着对象的建立而建立,存在于对象所在的堆内存中。

成员变量有默认初始化值。

局部变量:

局部变量只定义在局部范围内,如:函数内,语句内等。

局部变量存在于栈内存中。

作用的范围结束,变量空间会自动释放。

局部变量没有默认初始化值。

成员变量初始化

当一个对象被创建时,会对其中各种类型的成员变量自动进行初始化赋值。基本数据类型初始化值为0,引用数据类型初始化值为null

 SHAPE  \* MERGEFORMAT 

创建对象,使用对象

class Car// 对这类事物进行描述Car

{

String color = "red";

int num = 4;

void show() 

{

  System.out.println("color="+color+"..num="+num); 

}

}

class CarDemo 

public static void main(String[] args) 

Car c = new Car();//建立对象 

c.color = "black";//对对象的属性进行修改 

  c.show();// 使用对象的功能。 

}

}

对象内存结构

对象的产生

Person p = new Person();

这句话先在堆内存中创建了一个对象,然后栈内存中创建一个变量引用了对象的地址。

Car c1 = new Car();c1.color="blue";

Car c2 = new Car();


对象的生命周期

对象的生命周期从new关键字创建时开始,到没有任何引用到达对象时结束(成为垃圾)


匿名对象

我们可以不定义变量引用对象,使用new关键字创建对象后直接使用,这样的对象没有名字,所以叫匿名对象。

匿名对象因为没有任何引用到达,在使用一次之后即成为垃圾。

匿名对象是对象的简化形式

匿名对象两种使用情况

当对对象方法仅进行一次调用的时

匿名对象可以作为实际参数进行传递

封装(Encapsulation


封装:

是指隐藏对象的属性和实现细节,仅对外提供公共访问方式。

好处:

将变化隔离。

便于使用。

提高重用性。

提高安全性。

封装原则:

将不需要对外提供的内容都隐藏起来。

把属性都隐藏,提供公共方法对其访问。

private(私有)关键字


private关键字:

是一个权限修饰符。

用于修饰成员(成员变量和成员函数)

被私有化的成员只在本类中有效。

常用之一

将成员变量私有化,对外提供对应的set get 方法对其进行访问。

提高对数据访问的安全性。


private :私有,权限修饰符:用于修饰类中的成员(成员变量,成员函数)

私有只在本类中有效。

age私有化以后,类以外即使建立了对象也不能直接访问。

但是人应该有年龄,就需要在Person类中提供对应访问age的方式。

注意:私有仅仅是封装的一种表现形式。

之所以对外提供访问方式,就因为可以在访问方式中加入逻辑判断等语句

对访问的数据进行操作。提高代码健壮性

构造函数

什么是构造函数

构造函数(Constructor)是一个特殊的函数。


函数名和类名相同。

没有返回值类型。注意:没有返回值类型不等同于voidvoid也是一种返回值类型。不能使用return关键字返回任何值。

在使用new关键字创建对象之后自动调用。

构造函数的重载

构造函数的重载和普通函数相同,函数名相同,参数列表不同即可

构造函数的调用

构造函数在new关键字创建对象时调用。

构造函数可以在该类其他构造函数的第一个语句使用this关键字调用。

构造函数的访问权限

在定义构造函数时,如无特殊需要,应使用public关键字修饰构造函数。

在一些特定情况下,我们不想让别人创建该类对象,那么可以使用private修饰构造函数,例如单态设计模式。


特点:

函数名与类名相同

不用定义返回值类型

不可以写return语句

作用

给对象进行初始化。

注意:

默认构造函数的特点。

多个构造函数是以重载的形式存在的。

对象一建立就会调用与之对应的构造函数

构造函数的作用:可以用于给对象进行初始化。

构造函数的小细节

当一个类中没有定义构造函数时,那么系统会默认给该类加入一个空参数的构造函数。

当在类中自定义了构造函数后,默认的构造函数就没有了。

构造函数和一般函数在写法上有不同。

在运行上也有不同。

构造函数是在对象一建立就运行。给对象初始化。

而一般方法是对象调用才执行,给是对象添加对象具备的功能。

一个对象建立,构造函数只运行一次

而一般方法可以被该对象调用多次。

什么时候定义构造函数呢?

当分析事物时,该事物存在具备一些特性或者行为,那么将这些内容定义在构造函数中。

构造代码块{ 。。。}

作用:给对象进行初始化。

对象一建立就运行,而且优先于构造函数执行。

和构造函数的区别

构造代码块是给所有对象进行统一初始化,

而构造函数是给对应的对象初始化。

构造代码快中定义的是不同对象共性的初始化内容。

this关键字

特点:

this代表其所在函数所属对象的引用。

换言之

this代本类对象的引用。

什么时候使用this关键字呢

当在函数内需要用到调用该函数的对象时,就用this

例如:

this:看上去,是用于区分局部变量和成员变量同名情况。

this为什么可以解决这个问题?

this到底代表的是什么呢?

this:就代表本类的对象,到底代表哪一个呢?

this代表它所在函数所属对象的引用。

简单说:哪个对象在调用this所在的函数,this就代表哪个对象。

this的应用:当定义类中功能时,该函数内部要用到调用该函数的对象时,这时用this来表示这个对象。

但凡本类功能内部使用了了本类对象,都用this表示

this语句 :用于构造函数之间进行互相调用。

this语句只能定义在构造函数的第一行。因为初始化要先执行

函数的参数传递

基本数据类型的变量作为实参传入函数之后,在函数中将形参改变,调用处的实参不变。

因为基本数据类型的值是直接存在变量中,传入函数之后函数中的形参也同样存了一个值,这两个值是没有联系的,所以函数中将形参改变时修改的只是函数中的变量的值,和调用处的实参无关。

 

引用数据类型的变量作为实参传入函数之后,在函数中将形参改变,调用处的实参改变。

因为引用数据类型变量中存储的是地址,传入函数之后函数中的形参存储的也是同样一个地址,函数中将这个形参改变时改变的都是同一个地址上的对象,所以一边改变两边都变。

static 关键字

static 概述

static关键字用来修饰类的成员,被这个关键字修饰的成员都和类加载有关。

JVM运行时不会将所有类加载到内存,因为无法确定程序中要使用哪些。类在第一次使用时加载,只加载一次。

静态变量

static修饰的变量就是静态变量。


静态变量在类加载后就初始化。

静态变量被类的所有实例所共享

静态变量可以使用 类名.变量名 形式访问。


如果在定义一个类的时候,发现一个成员变量需要被所有实例所共享,那么这个成员变量就需要定义为static的。

静态方法

static修饰的方法就是静态方法。


静态方法在类加载后就可以使用

静态方法可以使用 类名.方法名 形式访问。

静态方法不能直接访问外部非静态成员。

因为外部非静态成员必须在类创建对象之后才能使用,而静态方法可以在没创建对象时就使用。

如果要在静态方法内部访问外部非静态成员,需要先创建该类对象,通过对象访问。

静态方法中不能使用this关键字

因为this是个引用,哪个对象调用方法就引用哪个对象。 而静态方法有可能不是被对象调用的,this无从引用。


如果一个方法不用访问对象的非静态成员,那么就可以定义为静态的,这样使用者就不需要创建对象,直接用类名调用。

静态方法通常是作为工具方法或者一个可以产生对象的方法被声明,目的是为了让调用者更方便的使用,不必创建对象。

static特点:

1,随着类的加载而加载。

   也就说:静态会随着类的消失而消失。说明它的生命周期最长。

2优先于的对象存在

明确一点:静态是先存在。对象是后存在的。

3,被所有对象所共享

4,可以直接被类名所调用

实例变量和类变量的区别:

1存放位置。

类变量随着类的加载而存在于方法区中

实例变量随着对象的建立而存在于堆内存中

2生命周期:

类变量生命周期最长,随着类的消失而消失。

实例变量生命周期随着对象的消失而消失

静态使用注意事项:

1静态方法只能访问静态成员。

非静态方法既可以访问静态也可以访问非静态。

2静态方法中不可以定义thissuper关键字。

因为静态优先于对象存在。所以静态方法中不可以出现this

3,主函数是静态的。

静态有利有弊

利处:对对象的共享数据进行单独空间的存储,节省空间。没有必要每一个对象中都存储一份。

可以直接被类名调用。

弊端:生命周期过长。

  访问出现局限性。(静态虽好,只能访问静态。)

java main函数

public static void main(String[] args) 

主函数:是一个特殊的函数。作为程序的入口,可以被jvm调用。

主函数的定义:

public:代表着该函数访问权限是最大的。

static:代表主函数随着类的加载就已经存在了。

void:主函数没有具体的返回值。

main:不是关键字,但是是一个特殊的单词,可以被jvm识别。

(String[] arr):函数的参数,参数类型是一个数组,该数组中的元素是字符串。字符串类型的数组。

主函数是固定格式的:jvm识别。

jvm在调用主函数时,传入的是new String[0];

static 用的时机

何时定义静态变量(类变量)呢?

当对象中出现共享数据时,该数据被静态所修饰。

对象中的特有数据要定义成非静态存在于堆内存中。

何时定义静态函数呢?

当功能内部没有访问到非静态数据(对象的特有数据),那么该功能可以定义成静态的。

静态的应用

每一个应用程序中都有共性的功能,

可以将这些功能进行抽取,独立封装。

以便复用。

javadoc 文档制作

javadoc -d myhelp -author -version ArrayTool.java


一个类中默认会有一个空参数的构造函数,

这个默认的构造函数的权限和所属类一致。

如果类被public修饰,那么默认的构造函数也带public修饰符。

如果类没有被public修饰,那么默认的构造函数,也没有public修饰。


默认构造构造函数的权限是随着的类的变化而变化的。


静态代码块

static修饰的代码块就是静态代码块。

静态代码块在类加载后执行。

静态代码块和静态方法相同,不能使用外部非静态成员。

静态代码块执行和静态变量的初始化顺序由代码从上到下顺序决定。


静态代码块。

格式:

static

{

静态代码块中的执行语句。

}


特点:随着类的加载而执行,只执行一次,并优先于主函数。

用于给类进行初始化的。


对象初始化过程

Person p = new Person("zhangsan",20);

该句话都做了什么事情?

1,因为new用到了Person.class.所以会先找到Person.class文件并加载到内存中。

2,执行该类中的static代码块,如果有的话,给Person.class类进行初始化。

3,在堆内存中开辟空间,分配内存地址。

4,在堆内存中建立对象的特有属性。并进行默认初始化。

5,对属性进行显示初始化。

6,对对象进行构造代码块初始化。

7,对对象进行对应的构造函数初始化。

8,将内存地址付给栈内存中的p变量。


对象调用方法过程?


静态内部类

static修饰的内部类就是静态内部类。

静态内部类在类加载后就可以创建对象,无需创建外部类对象。


垃圾回收

对象在没有任何引用可以到达时,生命周期结束,成为垃圾。


所有对象在被回收之前都会自动调用finalize()方法。


一个对象在成为垃圾之后不会被马上回收,JVM会检测内存中的垃圾堆积到一定程度时才会回收,如果我们不想等到这个时候才回收,可以使用System.gc()方法来通知虚拟机回收垃圾。调用该方法之后JVM会开启新线程做处理垃圾的工作,这需要一定时间。


单例设计模式(SingletonPattern

什么是设计模式

在编程过程中我们经常会遇到一些典型的问题或需要完成某种特定需求,而这些问题和需求前人也曾经遇到过,他们经过大量理论总结和实践验证之后优选出的代码结构、编程风格、以及解决问题的思考方式,这就是设计模式(Design pattern)。设计模式就像是经典的棋谱,不同的棋局,我们用不同的棋谱,免得我们自己再去思考和摸索。

单态(单例)设计模式

单态设计模式(Singleton pattern)就是要保证在整个程序中某个类只能存在一个对象,这个类不能再创建第二个对象。


想要保证对象唯一。

1,为了避免其他程序过多建立该类对象。先禁止其他程序建立该类对象

2,还为了让其他程序可以访问到该类对象,只好在本类中,自定义一个对象。

3,为了方便其他程序对自定义对象的访问,可以对外提供一些访问方式。

这三部怎么用代码体现呢

1,将构造函数私有化。

2,在类中创建一个本类对象。

3,提供一个方法可以获取到该对象。

对于事物该怎么描述,还怎么描述。

当需要将该事物的对象保证在内存中唯一时,就将以上的三步加上即可。

/*

这个是先初始化对象。

称为:饿汉式。


Single类一进内存,就已经创建好了对象。

class Single

{

private static Single s = new Single();

private Single(){}

public static Single getInstance()

{

return s;

}

}

*/


//对象是方法被调用时,才初始化,也叫做对象的延时加载。成为:懒汉式。

//Single类进内存,对象还没有存在,只有调用了getInstance方法时,才建立对象。

class Single

{

private static Single s = null;

private Single(){}

public static Single getInstance()

{

if(s==null)

{

synchronized(Single.class)

{

if(s==null)

s = new Single();

}

}

return s;

}

}


//记录原则:定义单例,建议使用饿汉式。


继承(inherit

 继承概述

什么是继承

在程序中,可以使用extends关键字可以让一个类继承另外一个类。

继承的类为子类(派生类),被继承的类为父类(超类, 基类)

子类会自动继承父类所有的方法和属性。

为什么要使用继承 

当我们发现一个类的功能不行,方法不够用时,就可以派生子类,增加方法。

当我们需要定义一个能实现某项特殊功能的类时,就可以使用继承。

最终还是为了一个目的,实现代码的复用性。


当我们定义一个类时,发现另一个类的功能这个类都需要,而这个类又要增加一些新功能时,就可以使用extends关键字继承那个类,这样那个被继承类的功能就都有了,不必重写编写代码。这时只要在新的类中编写新的功能即可,原有代码就被复用了。

继承的特点

子类中可以使用super调用父类成员

super用法和this类似,this是谁调用该方法就引用谁,super是调用该方法的对象的父类对象。


继承

1,提高了代码的复用性。

2,让类与类之间产生了关系。有了这个关系,才有了多态的特性。

注意:千万不要为了获取其他类的功能,简化代码而继承。

必须是类与类之间有所属关系才可以继承。所属关系 is a


Java语言中:java只支持单继承,不支持多继承


因为多继承容易带来安全隐患:当多个父类中定义了相同功能,

当功能内容不同时,子类对象不确定要运行哪一个。

但是java保留这种机制。并用另一种体现形式来完成表示。多实现。


java支持多层继承。也就是一个继承体系


如何使用一个继承体系中的功能呢?


想要使用体系,先查阅体系父类的描述,因为父类中定义的是该体系中共性功能。

通过了解共性功能,就可以知道该体系的基本功能。

那么这个体系已经可以基本使用了。

那么在具体调用时,要创建最子类的对象为什么呢?

一是因为有可能父类不能创建对象

二是创建子类对象可以使用更多的功能,包括基本的也包括特有的。


简单一句话:查阅父类功能,创建子类对象使用功能。


聚集has a


聚合:队员与球队的关系


组合:身体与心脏的关系


组合设计模式(CompositePattern)

什么时候用组合

组合是一种实现代码复用的方式,当我们在定义一个类的时候需要用到另外一个类的方法时,就可以用组合。

怎么用组合

定义一个所需要的类类型的成员变量

通过构造函数进行装配,接收一个该类类型的对象,用成员变量引用

在需要使用另一个类的方法时通过成员变量访问

组合的优点

如果两个类没有父子关系,不合适用继承。

Java只支持单继承,组合不占用继承位置。


 super 关键字

子父类出现后,类成员的特点:


类中成员:

1,变量。

2,函数。

3,构造函数。


1,变量

如果子类中出现非私有的同名成员变量时,

子类要访问本类中的变量,用this

子类要访问父类中的同名变量,用super


super的使用和this的使用几乎一致。

this代表的是本类对象的引用。

super代表的是父类对象的引用。


 函数覆盖(override

2,子父类中的函数


当子类出现和父类一模一样的函数时,

当子类对象调用该函数,会运行子类函数的内容。

如同父类的函数被覆盖一样。


这种情况是函数的另一个特性:重写(覆盖)


当子类继承父类,沿袭了父类的功能,到子类中,

但是子类虽具备该功能,但是功能的内容却和父类不一致,

这时,没有必要定义新功能,而是使用覆盖特殊,保留父类的功能定义,并重写功能内容。


覆盖:

1,子类覆盖父类,必须保证子类权限(方法的修饰符)大于等于父类权限,才可以覆盖,否则编译失败。

2静态只能覆盖静态


记住大家:

重载:只看同名函数的参数列表。

重写:子父类方法要一模一样。


3,子父类中的构造函数。


在对子类对象进行初始化时,父类的构造函数也会运行,

那是因为子类的构造函数默认第一行有一条隐式的语句 super();

super():会访问父类中空参数的构造函数。而且子类中所有的构造函数默认第一行都是super();


为什么子类一定要访问父类中的构造函?


因为父类中的数据子类可以直接获取。所以子类对象在建立时,需要先查看父类是如何对这些数据进行初始化的。

所以子类在对象初始化时,要先访问一下父类中的构造函数。

如果要访问父类中指定的构造函数,可以通过手动定义super语句的方式来指定。


注意:super语句一定定义在子类构造函数的第一行。


 子类实例化过程

子类的实例化过程。


结论:

子类的所有的构造函数,默认都会访问父类中空参数的构造函数

因为子类每一个构造函数内的第一行都有一句隐式super();


当父类中没有空参数的构造函数时,子类必须手动通过super语句形式来指定要访问父类中的构造函数。


当然:子类的构造函数第一行也可以手动指定this语句来访问本类中的构造函数。

子类中至少会有一个构造函数会访问父类中的构造函数。


 final 关键字

final : 最终。作为一个修饰符,

1,可以修饰类,函数,变量

2,被final修饰的类不可以被继承。为了避免被继承,被子类复写功能。

3,被final修饰的方法不可以被复写

4,被final修饰的变量是一个常量只能赋值一次既可以修饰成员变量,有又可以修饰局部变量。

当在描述事物时,一些数据的出现值是固定的,那么这时为了增强阅读性,都给这些值起个名字。方便于阅读。

而这个值不需要改变,所以加上final修饰。作为常量:常量的书写规范所有字母都大写,如果由多个单词组成。

单词间通过_连接。

5,内部类定义在类中的局部位置上是,只能访问该局部被final修饰的局部变量。


 抽象类

抽象类:

Java中可以定义没有方法体的方法,该方法的具体实现由子类完成,该方法称为抽象                 方法,包含抽象方法的类就是抽象类。

抽象方法的由来

多个对象都具备相同的功能,但是功能具体内容有所不同,那么在抽取过程中,只抽取  了功能定义,并未抽取功能主体,那么只有功 能声明,没有功能主体的方法称为抽象方法。

例如:狼和狗都有吼叫的方法,可是吼叫内容是不一样的。所以抽象出来的犬科虽然有吼叫功能,但是并不明确吼叫的细节。


当多个类中出现相同功能,但是功能主体不同,

这是可以进行向上抽取。这时,只抽取功能定义,而不抽取功能主体。


抽象:看不懂。

抽象类的特点

1抽象方法一定在抽象类中

2,抽象方法和抽象类都必须被abstract关键字修饰。

3,抽象类不可以用new创建对象。因为调用抽象方法没意义。

4,抽象类中的抽象方法要被使用,必须由子类复写所有的抽象方法后,建立子类对象调用。

如果子类只覆盖了部分抽象方法,那么该子类还是一个抽象类。


抽象类和一般类没有太大的不同。

该如何描述事物,就如何描述事物,只不过,该事物出现了一些看不懂的东西。

这些不确定的部分,也是该事物的功能,需要明确出现。但是无法定义主体。

通过抽象方法来表示。


抽象类比一般类多个了抽象函数。就是在类中可以定义抽象方法。

抽象类不可以实例化。


特殊:抽象类中可以不定义抽象方法,这样做仅仅是不让该类建立对象


练习:


abstract 关键字,和哪些关键字不能共存。

final:被final修饰的类不能有子类。而被abstract修饰的类一定是一个父类。

private: 抽象类中的私有的抽象方法,不被子类所知,就无法被复写。

而抽象方法出现的就是需要被复写。

static:如果static可以修饰抽象方法,那么连对象都省了,直接类名调用就可以了。

可是抽象方法运行没意义。


抽象类中是否有构造函数?

有,抽象类是一个父类,要给子类提供实例的初始化。


模板设计模式(TemplatePattern)

为什么要使用模板方法设计模式

在解决一些问题或者设计一个软件的时候,需要先定义一个模板,就相当于一种事先定义好的协议。

以后要做这系列的事情都按照这个模板来做。这样就实现统一化管理。

如何实现模板方法设计模式

定义一个抽象的父类做为模板,定义所有需要的方法

在父类中实现供外界调用的主方法,将方法声明为final

根据不同业务需求定义子类实现父类的抽象方法


 接口(interface

什么是接口

接口是一种特殊的抽象类,接口中声明的所有方法都是抽象的

使用interface关键字修饰一个接口

接口的用法

我们可以定义一个类来实现接口,使用implements关键字

实现一个接口需要实现接口中所有的方法,抽象类除外

通常使用匿名内部类来实现一个接口

接口可以继承接口(多继承),使用extends关键字。 接口不能继承抽象类,因为抽象类中可能有不抽象的方法。

一个类可以实现多个接口,为了实现多态

接口中的方法和变量

接口中定义的方法默认是公有的抽象的,被public abstract修饰

接口中定义的变量默认为全局常量,使用public static final修饰

abstract classinterface的区别

抽象类中可以有抽象的方法,接口中全是抽象的方法

抽象类用extends继承,接口用implements实现

抽象类中的变量和方法没有默认修饰符,接口中的变量默认为public static final的,接口中的方法默认为public abstract

一个类只能继承一个抽象类,一个类可以实现多个接口

什么时候用抽象类,什么时候用接口

如果能用接口,就不用抽象类,因为别人实现接口可以不占用继承的位置。

如果定义一个抽象的父类,其中所有方法都是抽象的,那么就定义为接口。

如果定义一个抽象的父类的时候,需要有不抽象的方法,那么只能定义为抽象类。


多态(Polymorphism

 多态概述

什么是多态

多态字面上的意思就是多种形态。在面向对象语言中,我们可以将函数的形参定义为一个父类类型,而在真正调用该函数时这个父类类型的所有子类对象都可以传入,根据传入的子类对象不同函数可以运行处多种形态。

多态的特点

应用程序不必为每一个派生类(子类)编写功能调用,只需要对抽象基类进行处理即可。这一招叫以不变应万变,可以大大提高程序的可复用性。

派生类的功能可以被基类的引用变量引用,这叫向后兼容,可以提高程序的可扩充性和可维护性。现在写的程序可以调用将来写的程序不足为奇


多态:可以理解为事物存在的多种体现形态。

Eg.

人:男人,女人

动物:猫,狗。

x = new ();

动物 x = new ();


一。表现:

父类或者接口的引用指向了或者接收了自己的子类对象。

二。前提:

1,类与类之间要有关系。继承,实现。

2,通常都会有覆盖。

三。好处:

预先定义的程序可以运行后期程序的内容。

增强了程序的扩展性。

四。弊端:

虽然可以预先使用,但是只能访问父类中已有的功能,运行的是后期子类的功能内容。

不能预先使用子类中定义的特有功能。

五。多态的注意事项

在代码中。

对于成员函数:Fu f = new Zi(); f.method();

编译时期:看左边。

运行时期:看右边。

因为成员函数有一个覆盖操作。

毕姥爷和毕老师的故事。

对于非私有的实例变量,

静态变量,静态方法。


编译和运行都看左边。

六。转型。

子类对象被父类引用:子类对象在向上转型。

将指向子类对象的父类应用转换成子类类型引用:向下转型。


毕姥爷和毕老师的故事。

class 毕姥爷

{}


class 毕老师 extends 毕姥爷

{}

毕姥爷 ly = new 毕老师();//毕老师向上转型为了毕姥爷。向上转型

毕老师 ls = (毕老师)ly; //将代表毕老师对象的父类引用ly强制转换成了毕老师类型。向下转型。

七。应用

电脑使用。主板运行。

 类型转换

向上转型

把一个子类当做父类来用是可以的,因为父类有的子类都有

把一个父类当做子类来用就不可以了,因为子类有的父类不一定有

可以定义一个父类类型的变量来记住子类对象,这在程序中称之为向上转型

强制类型转换

把一个子类当做父类来用的时候,不能调用子类特有方法。

因为编译时编译器会做语法检查,看到变量是父类类型那么就会到父类中查找是否有该方法,没有则报错。

这种情况下,就需要强制类型转换,将父类类型强转成子类类型。

(子类名)变量名形式进行强制类型转换

强制类型转换时,无论类型是否匹配编译都不会报错,但如果类型不匹配运行会报错,我们可以使用instanceof进行判断,编译时预知错误。


在子类当做父类来用时,不能调用特有方法,如果一定要调用,就需要强制类型转换回子类。在做转换时最好instanceof判断一下类型是否匹配。


//Animal a = new Cat();//类型提升。 向上转型。

//a.eat();


//如果想要调用猫的特有方法时,如何操作?

//强制将父类的引用。转成子类类型。向下转型。

///Cat c = (Cat)a;

//c.catchMouse();

//千万不要出现这样的操作,就是将父类对象转成子类类型。

//我们能转换的是父类应用指向了自己的子类对象时,该应用可以被提升,也可以被强制转换。

//多态自始至终都是子类对象在做着变化。

instanceof : 用于判断对象的类型。 对象 intanceof 类型(类类型 接口类型)  


 多态中成员的使用特点

在多态中成员函数的特点:

在编译时期:参阅引用型变量所属的类中是否有调用的方法。如果有,编译通过,如果没有编译失败。

在运行时期:参阅对象所属的类中是否有调用的方法。

简单总结就是:成员函数在多态调用时,编译看左边,运行看右边。


在多态中,成员变量的特点:

无论编译和运行,都参考左边(引用型变量所属的类)


在多态中,静态成员函数的特点

无论编译和运行,都参考做左边。


Object

object:是所有对象的直接后者间接父类,传说中的上帝。

该类中定义的肯定是所有对象都具备的功能。


Object类中已经提供了对对象是否相同的比较方法。


如果自定义类中也有比较相同的功能,没有必要重新定义。

只要沿袭父类中的功能,建立自己特有比较内容即可。这就是覆盖。

Object:java中所有对象的直接或者间接的父类。

它里面的方法都所有对象都具备的。

常见方法:

boolean equals(Object obj):用于比较两个对象是否相同。

String toString(): 获取对象的字符串表现形式 类名@哈希值  

getClass().getName()+"@"+Integer.toHexString(hashCode());

Class getClass():获取正在运行的对象所属的字节码文件的对象。也就是说如果Demo d = new Demo();

d.getClass():获取的就是d执行的对象所属的字节码文件Demo.class对象。

通常在自定义对象时,因为对象中都有自己特有的描述,

所以都会建立对象自身的特有比较方法,或者字符串表现形式。

也就是说,会覆盖Object中的方法。


内部类(InnerClass

内部类的访问规则

1,内部类可以直接访问外部类中的成员,包括私有。

之所以可以直接访问外部类中的成员,是因为内部类中持有了一个外部类的引用,格式 外部类名.this

2,外部类要访问内部类,必须建立内部类对象。


访问格式:

1,当内部类定义在外部类的成员位置上,而且非私有,可以在外部其他类中。

可以直接建立内部类对象。

格式

部类名.内部类名  变量名 = 外部类对象.内部类对象;

Outer.Inner in = new Outer().new Inner();


2,当内部类在成员位置上,就可以被成员修饰符所修饰。

比如,private:将内部类在外部类中进行封装。

static:内部类就具备static的特性。

当内部类被static修饰后,只能直接访问外部类中的static成员。出现了访问局限。


外部其他类中,如何直接访问static内部类的非静态成员呢?

new Outer.Inner().function();


在外部其他类中,如何直接访问static内部类的静态成员呢?

Outer.Inner.function();


注意:当内部类中定义了静态成员,该内部类必须是static的。

  当外部类中的静态方法访问内部类时,内部类也必须是static的。


当描述事物时,事物的内部还有事物,该事物用内部类来描述。

因为内部事务在使用外部事物的内容。


内部类定义在局部时

1,不可以被成员修饰符修饰

2,可以直接访问外部类中的成员,因为还持有外部类中的引用。

但是不可以访问它所在的局部中的变量。只能访问被final修饰的局部变量。


匿名内部类:

1,匿名内部类其实就是内部类的简写格式。

2,定义匿名内部类的前提:

内部类必须是继承一个类或者实现接口。

3匿名内部类的格式:  new 父类或者接口(){定义子类的内容}

4,其实匿名内部类就是一个匿名子类对象。而且这个对象有点胖。 可以理解为带内容的对象。

5,匿名内部类中定义的方法最好不要超过3个。


异常(Exception

什么是异常

异常就是Java程序在运行过程中出现的错误。如程序要打开一个不存的文件、网络连接中断、操作数组越界、装载一个不存在的类等。

Throwable

Throwable表示Java中可被抛出的对象,它是所有错误和异常的父类

Throwable有两个子类:ErrorException

Error表示错误

Exception表示异常

RuntimeException表示运行时异常,是Exception的子类


 SHAPE  \* MERGEFORMAT 

异常的分类

Error(错误)

Java虚拟机生成并抛出,包括动态链接失败、虚拟机错误等,程序对其不进行处理

Exception(异常

所有异常类的父类,子类定义了各种各样可能出现的异常事件,一般需要用户显式地声明向外抛出或捕获。

Runtime Exception(运行时异常)

一类特殊的异常,如被0除、数组角标越界等。产生比较频繁,处理麻烦,如果每次都处理,会对程序可读性和运行效率影响比较大,因此由系统检测并将它们交给缺省的异常处理程序,用户不必对其进行处理。这类异常不处理,编译时不会报错,只是在运行时出现错误时才报告异常,所以我们称之为运行时异常,所有RuntimeException的子类都是运行时异常。我们也可以对运行时异常进行处理。

编译时异常

Exception中除了RuntimeException的子类,其他异常都是必须要处理的,如果不处理,编译时会报错,这些异常我们称之为编译时异常。


异常体系的特点:异常体系中的所有类以及建立的对象都具备可抛性。

也就是说可以被throwthrows关键字所操作。

只有异常体系具备这个特点。



--------------

throwthrows的用法:


throw定义在函数内,用于抛出异常对象。

throws定义在函数上,用于抛出异常类,可以抛出多个用逗号隔开。



当函数内容有throw抛出异常对象,并未进行try处理。必须要在函数上声明,都在编译失败。

注意,RuntimeException除外。也就说,函数内如果抛出的RuntimeExcpetion异常,函数上可以不用声明。

--------------


如果函数声明了异常,调用者需要进行处理。处理方法可以throws可以try


--------------

异常处理语句:

try

{

需要被检测的代码;

}

catch ()

{

处理异常的代码;

}

finally

{

一定会执行的代码;

}


有三个结合格式:

1. try

{

}

catch ()

{

}


2. try

{

}

finally

{

}



3. try

{

}

catch ()

{

}

finally

{

}




注意:

1finally中定义的通常是 关闭资源代码。因为资源必须释放。

2finally只有一种情况不会执行。当执行到System.exit(0);fianlly不会执行。


--------------


自定义异常

定义类继承Exception或者RuntimeException

1,为了让该自定义类具备可抛性。

2,让该类具备操作异常的共性方法。


当要定义自定义异常的信息时,可以使用父类已经定义好的功能。

异常异常信息传递给父类的构造函数。

class MyException extends Exception

{

MyException(String message)

{

super(message);

}

}


自定义异常:按照java的面向对象思想,将程序中出现的特有问题进行封装。

--------------



异常的好处:

1,将问题进行封装。

2,将正常流程代码和问题处理代码相分离,方便于阅读。



异常的处理原则

1处理方式有两种:try 或者 throws

2,调用到抛出异常的功能时,抛出几个,就处理几个。

一个try对应多个catch

3多个catch,父类的catch放到最下面。

4catch内,需要定义针对性的处理方式。不要简单的定义printStackTrace,输出语句。

也不要不写。

当捕获到的异常,本功能处理不了时,可以继续在catch中抛出。

try

{

throw new AException();

}

catch (AException e)

{

throw e;

}


如果该异常处理不了,但并不属于该功能出现的异常。

可以将异常转换后,在抛出和该功能相关的异常。


或者异常可以处理,当需要将异常产生的和本功能相关的问题提供出去,

当调用者知道。并处理。也可以将捕获异常处理后,转换新的异常。

try

{

throw new AException();

}

catch (AException e)

{

// AException处理。

throw new BException();

}


比如,汇款的例子。


异常的注意事项

在子父类覆盖时:

1,子类抛出的异常必须是父类的异常的子类或者子集。

2,如果父类或者接口没有异常抛出时,子类覆盖出现异常,只能try不能抛。


Java中常用的包

java.lang

包含一些Java语言的核心类,如StringMathIntegerSystemThread,提供常用功能。

java.awt

包含了构成抽象窗口工具集(abstract window toolkits)的多个类,这些类被用来构建和管理应用程序的图形用户界面(GUI)

java.net

包含执行与网络相关的操作的类。 

java.io

包含能提供多种输入/输出功能的类。

java.util

包含一些实用工具类,如定义系统特性、使用与日期日历相关的函数。

定义带包类

使用package语句加上包名来定义类所属于的包,包名全部小写

package语句为Java源文件的第一条语句

如果一个类中没有使用package语句,这个类为缺省无包名

一个类如果想被其他包中的类引用,必须使用public关键字修饰。构造函数也需要public

如果一个类被声明为public,那么必须和文件名同名

使用带包的类

在使用带包的类时需要使用全限定名(包名.类名

在每次写类名时都使用全限定名很麻烦,我们可以使用import导入包,之后再使用就无需写包名了

星号*:导入一个包中所有类。优先匹配当前包中的类,如果当前包没有再匹配导入包中的类。

具体类名:导入指定一个类。无论当前包中是否有同名类,都直接匹配导入的类。

无包的类可以使用有包的类,有包的类不能使用无包的类

编译运行带包的类

编译一个带包的源文件,在生成class文件的同时需要生成包文件

编译命令:javac –d <目录> 源文件名.java

运行有包的类时需要加上包名

运行命令:java 包名.类名   


jar文件

什么是jar文件

jar文件是Java文件的一种压缩格式

一般来讲,我们会将一个软件系统的所有class文件打成一个jar文件以供别人使用

当我们用到jar包中的类时,需要将jar文件的绝对路径加到classpath当中

如何压缩jar文件

将编译好的带包的class文件压缩成一个jar文件称为打jar

jar命令jar cvf jar包名.jar 要打包的文件/文件夹

运行jar文件命令 java -jar jar文件名.jar


通过jar.exe工具对jar的操作。

创建jarjar -cvf mypack.jar packa packb

查看jarjar -tvf mypack.jar [>定向文件]

解压缩jar -xvf mypack.jar

自定义jar包的清单文件jar –cvfm mypack.jar mf.txt packa packb


访问控制符




类的访问控制符有两种:

public关键字修饰:可以被所有的类访问

缺省为default:只能被同一包中的类访问


多线程

多线程概述

进程


几乎所有操作系统都支持进程的概念,所有运行中的任务通常对应一条进程(Process )。当一个程序进入内存运行,即变成一个进程。进程是处于运行过程中的程序,并且具有一定独立功能,进程是系统进行资源分配和调度的一个独立单位

进程包含如下三个特征:

独立性:进程是系统中独立存在的实体,它可以拥有自己独立的资源,每一个进程都拥有自己私有的地址空间。在没有经过进程本身允许的情况下,一个用户进程不可以直接访问其他进程的地址空间。

动态性:进程与程序的区别在于,程序只是一个静态的指令集合,而进程是一个正在系统中活动的指令集合。在进程中加入了时间的概念。进程具有自己的生命周期和各种不同的状态,这些概念在程序中都是不具备的。

并发性:多个进程可以在单个处理器上并发执行,多个进程之间不会互相影响。


区别:

并发性(concurrency)和并行性(parallel )是两个概念,并行指在同一时刻,有多条指令多个处理器上同时执行;并发指在同一时刻只能有一条指令执行,但多个进程指令被快速轮换执行,使得在宏观上具有多个进程同时执行的效果。


多线程则扩展了多进程的概念,使得同一个进程可以同时并发处理多个任务。线程(Thread )也被称作轻量级进程(Lightweight Process )线程是进程的执行单元。就像进程在操作系统中的地位一样,线程在程序中是独立的、并发的执行流。当进程被初始化后,主线程就被创建了。对于绝大多数的应用程序来说,通常仅要求有一个主线程,但我们也可以在该进程内创建多条顺序执行流,这些顺序执行流就是线程每条线程也是互相独立的

线程是进程的组成部分,一个进程可以拥有多个线程,一个线程必须有一个父进程线程可以拥有自己的堆栈、自己的程序计数器和自己的局部变量,但不再拥有系统资源,它与父进程的其他线程共享该进程所拥有的全部资源。因为多个线程共享父进程里的全部资源,因此编程更加方便;但必须更加小心,我们必须确保线程不会妨碍同一进程里的其他线程。

一个程序运行后至少有一个进程,一个进程里可以包含多个线程,但至少要包含一个线程。

线程共享的环境包括:进程代码段、进程的公有数据等。利用这些共享的数据等,线程很容易实现相互之间的通信。

多线程编程包含如下几个优点:

进程间不能共享内存,但线程之间共享内存非常容易。

系统创建进程需要为该进程重新分配系统资源,但创建线程则代价小得多,因此使用多线程来实现多任务并发比多进程的效率高

Java语言内置多线程功能支持,而不是单纯地作为底层操作系统的调度方式,从而简化了Java的多线程编。


线程的创建和启动

Java使用Thread类代表线程,所有的线程对象都必须是Thread类或其子类的实例。每条线程的作用是完成一定的任务,实际上就是执行一段程序流(一段顺序执行的代码)Java使用run方法来封装这样一段程序流


继承Thread类创建线程类


通过继承Thread类来创建并启动多线程的步骤如下:

1.定义Thread类的子类,并重写该类的run方法,该run方法的方法体就是代表了线程需要完成的任务。因此,我们经常把run方法称为线程执行体。

2.创建Thread子类的实例,即使创建了线程对象。

3.用线程对象的start方法来启动该线程。


实现Runnable接口创建线程类


实现Runnable接口来创建并启动多条线程的步骤如下:

1.定义Runnab 1e接口的实现类,并重写该接口的run方法,该run方法的方法体同样是该线程的线程执行体

2.创建Runnable实现类的实例,并以此实例作为Threadtarget来创建Thread对象,该Thread对象才是真正的线程对象。


使用Runnable接口创建线程可以共享同一个线程类实例的资源。


两种方式所创建线程的对比

采用实现Runnable接口方式的多线程:

线程类只是实现了Runnable接口,还可以继承其他类

在这种方式下,可以多个线程共享同一个target对象,所以非常适合多个相同线程来处理同一份资源的情况,从而可以将CPU、代码和数据分开,形成清晰的模型,较好地体现了面向对象的思想。

劣势是:编程稍稍复杂,如果需要访问当前线程,必须使用Thread.currentThread()方法。


采用继承Thread类方式的多线程:

劣势是:因为线程类已经继承了Thread类,所以不能再继承其他父类。

优势是:编写简单,如果需要访问当前线程,无须使用Thread.currentThread()方法,直接使用this即可获得当前线程

实际上几乎所有的多线程应用都可采用第一种方式,也就是实现Runnable接口的方式。


线程的生命周期

当线程被创建并启动以后,它既不是一启动就进入了执行状态,也不是一直处于执行状态,在线程的生命周期中,它要经过新建(New)、就绪(Runnable)、运行(Running )、阻塞(Blocked)和死亡(Dead )五种状态。尤其是当线程启动以后,它不能一直霸占CPU独自运行,所以CPU需要在多条线程之间切换,于是线程状态也会多次在运行、阻塞之间切换。


新建和就绪状态


当程序使用new关键字创建了一个线程之后,该线程就处于新建状态,此时它和其他Java对象一样,仅仅由Java虚拟机为其分配了内存,并初始化了其成员变量的值。此时的线程对象没有表现出任何线程的动态特征,程序也不会执行线程执行体中的线程执行体。

当线程对象调用了start()方法之后,该线程处于就绪状态Java虚拟机会为其创建方法调用栈和程序计数器,处于这个状态中的线程并没有开始运行,它只是表示该线程可以运行了。至于该线程何时开始运行取决于jvm里线程调度器的调度


不要对已经处于启动状态的线程再次调用start()方法,否则将引发

IllegalThreadStateException异常。


运行和阻塞状态

如果处于就绪状态的线程获得了CPU,开始执行run方法的线程执行体,则该线程处于运行状态,如果计算机只有一个CPU,在任何时刻只有一条线程处于运行状态。当然,在一个多处理器的机器上,将会有多个线程并行(注意是并行:parallel)执行;但当线程数大于处理器数时,依然会有多条线程在同一个CPU上轮换的现象。

当发生如下情况下,线程将会进入阻塞状态:

线程调用sleep方法主动放弃所占用的处理器资源。

线程调用了一个阻塞式io方法,在该方法返回之前,该线程被阻塞。

线程试图获得一个同步监视器.但该同步监视器正被其他线程所持有。

线程在等待某个通知(notify )

程序调用了线程的suspend方法将该线程挂起。不过这个方法容易导致死锁,所以程序应该尽量避免使用该方法。



线程状态转换图:


当运行状态的线程失去处理器资源时,该线程进入就绪状态。但有一个方法例外,调用yield()可以让当前处于运行状态的线程转入就绪状态.


线程死亡

线程会以以下三种方式之一结束,结束后就处于死亡状态:

run()方法执行完成,线程正常结束。

线程抛出一个未捕获的ExceptionError.

真接调用该线程的stop()方法来结束该线程该方法容易导致死锁,通常不推荐使用。


注:

主线程结束时候,其他线程不受任何影响,并不会随之结束。一旦子线程启动起来后,它就拥有和主线程相同的地位,它不会受主线程的影响。



进程:是一个正在执行中的程序。

每一个进程执行都有一个执行顺序。该顺序是一个执行路径,或者叫一个控制单元。


线程:就是进程中的一个独立的控制单元。

线程在控制着进程的执行。


一个进程中至少有一个线程。


Java VM  启动的时候会有一个进程java.exe.


该进程中至少一个线程负责java程序的执行。

而且这个线程运行的代码存在于main方法中。

该线程称之为主线程。


扩展:其实更细节说明jvmjvm启动不止一个线程,还有负责垃圾回收机制的线程。


1,如何在自定义的代码中,自定义一个线程呢?


通过对api的查找,java已经提供了对线程这类事物的描述。就Thread类。


创建线程的第一种方式:继承Thread类。

步骤:

1,定义类继承Thread

2,复写Thread类中的run方法。

目的:将自定义代码存储在run方法。让线程运行。

3,调用线程的start方法,

该方法两个作用:启动线程,调用run方法。


发现运行结果每一次都不同。

因为多个线程都获取cpu的执行权。cpu执行到谁,谁就运行。

明确一点,在某一个时刻,只能有一个程序在运行。(多核除外)

cpu在做着快速的切换,以达到看上去是同时运行的效果。

我们可以形象把多线程的运行行为在互相抢夺cpu的执行权。


这就是多线程的一个特性:随机性。谁抢到谁执行,至于执行多长,cpu说的算。


为什么要覆盖run方法呢?


Thread类用于描述线程。

该类就定义了一个功能,用于存储线程要运行的代码。该存储功能就是run方法。


也就是说Thread类中的run方法,用于存储线程要运行的代码。


创建线程的第二种方式:实现Runable接口

步骤:

1,定义类实现Runnable接口

2,覆盖Runnable接口中的run方法。

将线程要运行的代码存放在该run方法中。

3,通过Thread类建立线程对象。

4,将Runnable接口的子类对象作为实际参数传递给Thread类的构造函数。

为什么要将Runnable接口的子类对象传递给Thread的构造函数。

因为,自定义的run方法所属的对象是Runnable接口的子类对象。

所以要让线程去指定指定对象的run方法。就必须明确该run方法所属对象。

5,调用Thread类的start方法开启线程并调用Runnable接口子类的run方法。

实现方式和继承方式有什么区别呢?

实现方式好处:避免了单继承的局限性。

在定义线程时,建立使用实现方式。


两种方式区别

继承Thread:线程代码存放Thread子类run方法中。

实现Runnable,线程代码存在接口的子类的run方法。


线程安全

通过分析,发现,打印出0-1-2等错票。


多线程的运行出现了安全问题。

问题的原因:

当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分,还没有执行完,

另一个线程参与进来执行。导致共享数据的错误。

解决办法:

对多条操作共享数据的语句,只能让一个线程都执行完。在执行过程中,其他线程不可以参与执行。

Java对于多线程的安全问题提供了专业的解决方式


就是同步代码块


synchronized(对象)

{

需要被同步的代码


}

对象如同锁。持有锁的线程可以在同步中执行。

没有持有锁的线程即使获取cpu的执行权,也进不去,因为没有获取锁。


火车上的卫生间---经典。


同步的前提:

1,必须要有两个或者两个以上的线程。

2,必须是多个线程使用同一个锁。


必须保证同步中只能有一个线程在运行。

好处:解决了多线程的安全问题。


弊端:多个线程需要判断锁,较为消耗资源,


同步函数用的是哪一个锁呢?

函数需要被对象调用。那么函数都有一个所属对象引用。就是this

所以同步函数使用的锁是this

如果同步函数被静态修饰后,使用的锁是什么呢?


通过验证,发现不在是this。因为静态方法中也不可以定义this


静态进内存是,内存中没有本类对象,但是一定有该类对应的字节码文件对象。

类名.class  该对象的类型是Class


静态的同步方法,使用的锁是该方法所在类的字节码文件对象。 类名.class


如何找问题:

1,明确哪些代码是多线程运行代码。

2,明确共享数据。

3,明确多线程运行代码中哪些语句是操作共享数据的。


死锁

当两个线程相互等待对方释放同步监视器时就会发生死锁Java虚拟机没有监测,也没有采用措施来处理死锁情况,所以多线程编程时应该采取措施避免死锁的出现。一旦出现死锁,整个程序既不会发生任何异常,也不会给出任何提示,只是所有线程处于阻塞状态,无法继续。

同步中嵌套同步。

线程通信

在同步代码中可以使用锁对象的wait()方法让当前线程等待

使用锁对象的notify()方法可以将正在等待的线程唤醒

如果多个线程都在等待,notify()唤醒随机1

notifyAll()方法可以唤醒所有在等待的线程

 等待唤醒机制

//notifyAll();


/*

wait:

notify();

notifyAll();


都使用在同步中,因为要对持有监视器()的线程操作。

所以要使用在同步中,因为只有同步才具有锁。


为什么这些操作线程的方法要定义Object类中呢?

因为这些方法在操作同步中线程时,都必须要标识它们所操作线程只有的锁,

只有同一个锁上的被等待线程,可以被同一个锁上notify唤醒。

不可以对不同锁中的线程进行唤醒。


也就是说,等待和唤醒必须是同一个锁。


而锁可以是任意对象,所以可以被任意对象调用的方法定义Object类中。


 生产者、消费者

对于多个生产者和消费者。

为什么要定义while判断标记。

原因:让被唤醒的线程再一次判断标记。


为什么定义notifyAll

因为需要唤醒对方线程。

因为只用notify,容易出现只唤醒本方线程的情况。导致程序中的所有线程都等待。


JDK1.5 中提供了多线程升级解决方案。

将同步Synchronized替换成现实Lock操作。

Object中的waitnotify notifyAll,替换了Condition对象。

该对象可以Lock 进行获取。

该示例中,实现了本方只唤醒对方操作。


Lock:替代了Synchronized

lock 

unlock

newCondition()


Condition:替代了Object wait notify notifyAll

await();

signal();

signalAll();

 Thread中的一些方法

stop方法已经过时。


如何停止线程?

只有一种,run方法结束。

开启多线程运行,运行代码通常是循环结构。


只要控制住循环,就可以让run方法结束,也就是线程结束。


特殊情况:

当线程处于了冻结状态。

就不会读取到标记。那么线程就不会结束。


当没有指定的方式让冻结的线程恢复到运行状态是,这时需要对冻结进行清除。

强制让线程恢复到运行状态中来。这样就可以操作标记让线程结束。


Thread类提供该方法 interrupt();


join:

A线程执行到了B线程的.join()方法时,A就会等待。等B线程都执行完,A才会执行。

join可以用来临时加入线程执行。

yield:

暂停当前正在执行的线程对象,并执行其他线程。


后台线程有个特征:如果所有的前台线程都死亡,后台线程会自动死亡。

String

什么是API

API全名:Application Programming InterfaceAPI是应用程序编程接口,指一些预先定义好的类。

例如我们想要一台电脑,并不需要自己生产每个零件,只要从各个厂商买到组装电脑的零件就可以,然后根据说明书学会使用,将零件安装在一起就得到了电脑。电脑就像是我们要的程序,而零件就是API,说明书就是帮助文档。

Java API

Java API就是Sun公司提供给我们使用的类,这些类将底层的实现封装了起来,我们不需要关心这些类是如何实现的,只需要学习这些类如何使用。

我们可以通过查帮助文档来了解Java提供的API如何使用

Java中常用API

String

对字符串进行操作通常我们使用String类,相关的还有StringBufferStringBuilder

集合类

集合是一种容器,用来存取对象(CollectionMap)

包装类

Java定义了一组包装类对基本数据类型进行了包装(IntegerDoubleBoolean)

时间对象

Java定义了一些类方便用户对时间、日期进行处理(DateCalendar)

系统类

Java定义了一些类针对系统进行操作(SystemRuntime)

IO

Java定义了一些类对数据传输进行了封装(输入输出流、File文件对象)

Socket

Java定义了一些类方便用户进行网络编程(SocketDatagramSocket)

String对象的存储

字符串是常量,一旦创建不能被修改。

字符串在程序中经常使用,虚拟机会将其缓存在String池中。

了解 String s = “abc” String s = new String(“abc”) 的区别。


String中常用方法


String类适用于描述字符串事物。

那么它就提供了多个方法对字符串进行操作。


常见的操作有哪些?

"abcd"


1,获取

1.1 字符串中的包含的字符数,也就是字符串的长度。

int length():获取长度。

1.2 根据位置获取位置上某个字符。

char charAt(int index):

1.3 根据字符获取该字符在字符串中位置。

int indexOf(int ch):返回的是ch在字符串中第一次出现的位置。

int indexOf(int ch, int fromIndex) :fromIndex指定位置开始,获取ch在字符串中出现的位置。


int indexOf(String str):返回的是str在字符串中第一次出现的位置。

int indexOf(String str, int fromIndex) :fromIndex指定位置开始,获取str在字符串中出现的位置。


int lastIndexOf(int ch)


2,判断。

2.1 字符串中是否包含某一个子串。

boolean contains(str):

特殊之处:indexOf(str):可以索引str第一次出现位置,如果返回-1.表示该str不在字符串中存在。

所以,也可以用于对指定判断是否包含。

if(str.indexOf("aa")!=-1)


而且该方法即可以判断,有可以获取出现的位置。


2.2 字符中是否有内容。

boolean isEmpty(): 原理就是判断长度是否为0. 

2.3 字符串是否是以指定内容开头。

boolean startsWith(str);

2.4 字符串是否是以指定内容结尾。

boolean endsWith(str);

2.5 判断字符串内容是否相同。复写了Object类中的equals方法。

boolean equals(str);

2.6 判断内容是否相同,并忽略大小写。

boolean equalsIgnoreCase();

3,转换。

3.1 将字符数组转成字符串。

构造函数:String(char[])

  String(char[],offset,count):将字符数组中的一部分转成字符串。


静态方法:

static String copyValueOf(char[]);

static String copyValueOf(char[] data, int offset, int count) 


static String valueOf(char[]):


3.2 将字符串转成字符数组。**

char[] toCharArray():


3.3 将字节数组转成字符串。

String(byte[])

String(byte[],offset,count):将字节数组中的一部分转成字符串。


3.4 将字符串转成字节数组。

byte[]  getBytes():

3.5 将基本数据类型转成字符串。

static String valueOf(int)

static String valueOf(double)


//3+"";//String.valueOf(3);


特殊:字符串和字节数组在转换过程中,是可以指定编码表的。


4,替换

String replace(oldchar,newchar);


5,切割

String[] split(regex);


6,子串。获取字符串中的一部分。

String substring(begin);

String substring(begin,end);


7,转换,去除空格,比较。

7.1 将字符串转成大写或则小写。

String toUpperCase();

String toLowerCase();


7.2 将字符串两端的多个空格去除。

String trim();


7.3 对两个字符串进行自然顺序的比较。

int compareTo(string);

StringBuffer StringBuilder

StringBuffer是字符串缓冲区。


是一个容器。

特点:

1,长度是可变化的。

2,可以字节操作多个数据类型。

3,最终会通过toString方法变成字符串。


C create U update R read D delete


1,存储。

StringBuffer append():将指定数据作为参数添加到已有数据结尾处。

StringBuffer insert(index,数据):可以将数据插入到指定index位置。


2,删除。

StringBuffer delete(start,end):删除缓冲区中的数据,包含start,不包含end

StringBuffer deleteCharAt(index):删除指定位置的字符。

3,获取

char charAt(int index) 

int indexOf(String str) 

int lastIndexOf(String str) 

int length() 

String substring(int start, int end) 

 

4,修改。

StringBuffer replace(start,end,string);

void setCharAt(int index, char ch) ;


5,反转

StringBuffer reverse();

 

6

将缓冲区中指定数据存储到指定字符数组中。

void getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin) 


JDK1.5 版本之后出现了StringBuilder.


StringBuffer是线程同步。

StringBuilder是线程不同步。


以后开发,建议使用StringBuilder


升级三个因素:

1,提高效率。

2,简化书写。

3,提高安全性。



基本数据类型包装类

基本数据类型对象包装类。


byte Byte

short Short

int       Integer

long     Long

Boolean  Boolean

Float  Float

double Double

char         Character


基本数据类型对象包装类的最常见作用

就是用于基本数据类型和字符串类型之间做转换


基本数据类型转成字符串。


基本数据类型+""


基本数据类型.toString(基本数据类型值);


如: Integer.toString(34);//34整数变成"34";



字符串转成基本数据类型


xxx a = Xxx.parseXxx(String);


int a = Integer.parseInt("123");


double b = Double.parseDouble("12.23");


boolean b = Boolean.parseBoolean("true");


Integer i = new Integer("123");


int num = i.intValue();




十进制转成其他进制。

toBinaryString();

toHexString();

toOctalString();



其他进制转成十进制。

parseInt(string,radix);



JDK1.5版本以后出现的新特性。

Integer x = 4;//自动装箱。//new Integer(4)


x = x/* x.intValue() */ + 2;//x+2:x 进行自动拆箱。变成成了int类型。和2进行加法运算。

//再将和进行装箱赋给x



Integer m = 128;

Integer n = 128;


sop("m==n:"+(m==n));


Integer a = 127;

Integer b = 127;


sop("a==b:"+(a==b));//结果为true。因为ab指向了同一个Integer对象。

//因为当数值在byte范围内容,对于新特性,如果该数值已经存在,则不会在开辟新的空间。

集合

集合概述

为什么出现集合类?

在面向对象的编程思想中,都是以对象的形式对事物进行描述的,为了保证对象的生命周期,我们需要持有对象

在很多情况下,我们不知道在程序中需要创建多少个对象,这时就不能依靠定义引用对象的变量来持有每一个对象

存储对象的容器就能帮我们解决这样的问题,而集合便是这样的容器

数组和集合类的区别

数组和集合类都是容器,都能存储对象

集合类的优势就在于长度可变

集合类的特点

集合类可用于存储对象

集合类的长度可变

一个集合可以存储多种类型的对象

集合接口

Collection接口

一个独立的元素的序列,这些元素服从一条或多条规则

Collection接口下主要分为List集合和Set集合

List集合的特点是元素有序、允许有重复元素

Set集合的特点是元素无存储顺序、不允许有重复元素


Collection定义了集合框架的共性功能。

1添加

add(e);

addAll(collection);


2删除

remove(e);

removeAll(collection);

clear();


3判断。

contains(e);

isEmpty();


4获取

iterator();

size();


5获取交集

retainAll();


6集合变数组

toArray();


1add方法的参数类型是Object。以便于接收任意类型对象。


2集合中存储的都是对象的引用(地址)

Map接口

一组成对的键值对对象,允许根据键来查找值

Map集合的键不允许有重复,所以Map的所有键构成了一个Set集合

主要学习HashMapTreeMap

Iterable接口

JDK1.5新定义的接口作为Collection的父接口

主要为了实现增强for循环


什么是迭代器呢?

其实就是集合的取出元素的方式。

如同抓娃娃游戏机中的夹子。


迭代器是取出方式,会直接访问集合中的元素。

所以将迭代器通过内部类的形式来进行描述。

通过容器的iterator()方法获取该内部类的对象。


List

List特点

元素有序,可重复。

我们主要学习三种:ArrayListVectorLinkedList

这三种都是List接口的实现类,使用上完全一样,只是实现原理不同,效率不同。

List集合判断元素是否相同,依据是元素的equals方法。

ArrayList

底层数组实现

查找快,增删慢

线程不安全

Vector

ArrayList基本一样

线程安全(线程同步),效率低


枚举就是Vector特有的取出方式。

发现枚举和迭代器很像。

其实枚举和迭代是一样的。


因为枚举的名称以及方法的名称都过长。

所以被迭代器取代了。

枚举郁郁而终了。

LinkedList

底层链表实现

增删块,查找慢


LinkedList:特有方法:

addFirst();

addLast();


getFirst();

getLast();

获取元素,但不删除元素。如果集合中没有元素,会出现NoSuchElementException


removeFirst();

removeLast();

获取元素,但是元素被删除。如果集合中没有元素,会出现NoSuchElementException



JDK1.6出现了替代方法。


offerFirst();

offerLast();



peekFirst();

peekLast();

获取元素,但不删除元素。如果集合中没有元素,会返回null


pollFirst();

pollLast();

获取元素,但是元素被删除。如果集合中没有元素,会返回null

存取元素

List集合元素存取方法一致

使用add()方法增加元素

由于List集合有序,可以使用get()方法获取元素

元素的迭代(Iterator)

通过集合对象的iterator()方法获得迭代器Iterator

通过Iterator迭代器的hasNext()方法判断是否存在下一个元素

通过Iterator迭代器的next()方法获取下一个元素

元素的迭代(Enumeration)

迭代Vector集合中的元素可以使用Enumeration

通过EnumerationhasMoreElements()方法判断是否还有元素

通过EnumerationnextElement()方法返回下一个元素


Collection

|--List:元素是有序的,元素可以重复。因为该集合体系有索引。

|--ArrayList:底层的数据结构使用的是数组结构。特点:查询速度很快。但是增删稍慢。线程不同步。

|--LinkedList:底层使用的链表数据结构。特点:增删速度很快,查询稍慢。线程不同步。

|--Vector:底层是数组数据结构。线程同步。被ArrayList替代了。因为效率低。



|--Set:元素是无序,元素不可以重复。、



List

特有方法。凡是可以操作角标的方法都是该体系特有的方法。


add(index,element);

addAll(index,Collection);


remove(index);


set(index,element);

get(index):

subList(from,to);

listIterator();

int indexOf(obj):获取指定元素的位置。

ListIterator listIterator();


List集合特有的迭代器。ListIteratorIterator的子接口。


在迭代时,不可以通过集合对象的方法操作集合中的元素。

因为会发生ConcurrentModificationException异常。


所以,在迭代器时,只能用迭代器的放过操作元素,可是Iterator方法是有限的,

只能对元素进行判断,取出,删除的操作,

如果想要其他的操作如添加,修改等,就需要使用其子接口,ListIterator


该接口只能通过List集合的listIterator方法获取。


Set

Set集合无序,不允许有重复元素

Set集合通过存入对象的equals方法来保证集合中没有重复元素

HashSet

HashSetSet的子类,因此也没有重复元素

底层使用哈希算法保证没有重复元素

存储对象时,先调用对象的hashCode()方法计算一个哈希值,在集合中查找是否有哈希值相同的对象。

如果没有哈希值相同的对象,直接存入。

如果有哈希值相同的对象,则和哈希值相同的对象进行equals()方法比较

equals()方法比较结果相同则不存,不同就存入。

HashSet集合里存储的对象必须正确重写hashCodeequals方法

TreeSet

TreeSet集合通过二叉树算法保证无重复元素,并对元素进行排序

在使用TreeSet时必须指定比较的算法,指定的方式有两种:

自然顺序:将要存储的类实现Comparable接口,重写compareTo方法,在方法中指定算法

比较器顺序:在创建TreeSet时,传入一个比较器Comparator,在比较器的compare方法中指定算法


--Set元素是无序(存入和取出的顺序不一定一致),元素不可以重复。、

|--HashSet:底层数据结构是哈希表。是线程不安全的。不同步。

HashSet是如何保证元素唯一性的呢?

是通过元素的两个方法,hashCodeequals来完成

如果元素的HashCode值相同,才会判断equals是否为true

如果元素的hashcode值不同,不会调用equals


注意,对于判断元素是否存在,以及删除等操作,依赖的方法是元素的hashcodeequals方法。



|--TreeSet可以对Set集合中的元素进行排序。

底层数据结构是二叉树。

保证元素唯一性的依据:

compareTo方法return 0.


TreeSet排序的第一种方式:让元素自身具备比较性。

元素需要实现Comparable接口,覆盖compareTo方法。

也种方式也成为元素的自然顺序,或者叫做默认顺序。


TreeSet的第二种排序方式。

当元素自身不具备比较性时,或者具备的比较性不是所需要的。

这时就需要让集合自身具备比较性。

在集合初始化时,就有了比较方式。


当元素自身不具备比较性,或者具备的比较性不是所需要的。

这时需要让容器自身具备比较性。

定义了比较器,将比较器对象作为参数传递给TreeSet集合的构造函数。


当两种排序都存在时,以比较器为主。


定义一个类,实现Comparator接口,覆盖compare方法。


Set集合的功能和Collection是一致的。


泛型

泛型

由于集合可以存储不同类型的数据,所以取元素时有可能会导致类型转换错误

JDK1.5增加了新特性泛型,为了减少操作集合时出错的几率

集合一旦声明了泛型,便只能存储同一类型的对象了


好处

1.将运行时期出现问题ClassCastException,转移到了编译时期。,

方便于程序员解决问题。让运行时问题减少,安全。,

2,避免了强制转换麻烦。



泛型格式:通过<>来定义要操作的引用数据类型。


在使用java提供的对象时,什么时候写泛型呢?

通常在集合框架中很常见,

只要见到<>就要定义泛型。


其实<> 就是用来接收类型的。


当使用集合时,将集合中要存储的数据类型作为参数传递到<>中即可。


泛型类。

什么时候定义泛型类?

当类中要操作的引用数据类型不确定的时候,

早期定义Object来完成扩展。

现在定义泛型来完成扩展。

class Utils<QQ>

{

private QQ q;

public void setObject(QQ q)

{

this.q = q;

}

public QQ getObject()

{

return q;

}

}



泛型类定义的泛型,在整个类中有效。如果被方法使用,

那么泛型类的对象明确要操作的具体类型后,所有要操作的类型就已经固定了。

为了让不同方法可以操作不同类型,而且类型还不确定。

那么可以将泛型定义在方法上。



特殊之处

静态方法不可以访问类上定义的泛型。

如果静态方法操作的应用数据类型不确定,可以将泛型定义在方法上。


泛型定义在接口上。

interface Inter<T>

{

void show(T t);

}


? 通配符。也可以理解为占位符。

泛型的限定;

extends E: 可以接收E类型或者E的子类型。上限。

super E: 可以接收E类型或者E的父类型。下限

Map

Map集合的特点

Map存储了一系列键值的映射关系

Map集合需要保证键的唯一性

可以通过键获得值,反之则不能

Map集合存储元素使用put(key,value)方法

HashMap

线程不安全,存取速度快,允许存放null键,null值。 

通过HashSet原理保证键唯一性

Hashtable

线程安全,速度慢,不允许存放null键,null,已被HashMap替代。

TreeMap

通过二叉树算法保证键唯一性

对键进行排序,排序原理与TreeSet相同。

Properties

Hashtable的子类,所以也是线程安全的

用于读写配置文件的,一般配置项等号两边都是String,所以该集合中的两列保存的都是String类型的数据

这个集合中只能存String,所以不需要定义泛型。


Map集合:该集合存储键值对。一对一对往里存。而且要保证键的唯一性。

1,添加。

put(K key, V value) 

putAll(Map<? extends K,? extends V> m) 


2,删除。

clear() 

remove(Object key) 


3,判断。

containsValue(Object value) 

containsKey(Object key) 

isEmpty() 



4,获取

get(Object key) 

size() 

values() 


entrySet() 

keySet() 


Map

|--Hashtable:底层是哈希表数据结构,不可以存入nullnull值。该集合是线程同步的。jdk1.0.效率低。

|--HashMap:底层是哈希表数据结构,允许使用 null 值和 null 键,该集合是不同步的。将hashtable替代,jdk1.2.效率高。

|--TreeMap:底层是二叉树数据结构。线程不同步。可以用于给map集合中的键进行排序。



Set很像。

其实大家,Set底层就是使用了Map集合。


map集合的两种取出方式

1Set<k> keySetmap中所有的键存入到Set集合。因为set具备迭代器。

所有可以迭代方式取出所有的键,在根据get方法。获取每一个键对应的值。


Map集合的取出原理:将map集合转成set集合。在通过迭代器取出。



2Set<Map.Entry<k,v>> entrySetmap集合中的映射关系存入到了set集合中,

而这个关系的数据类型就是:Map.Entry


Entry其实就是Map中的一个static内部接口。

为什么要定义在内部呢?

因为只有有了Map集合,有了键值对,才会有键值的映射关系。

关系属于Map集合中的一个内部事物。

而且该事物在直接访问Map集合中的元素。

Map.Entry 其实Entry也是一个接口,它是Map接口中的一个内部接口。


工具类

 Collections

Collections

工具类,提供了对集合的常用操作

对集合进行查找

取出集合中的最大值,最小值

List集合进行排序


集合框架的工具类。

Collections:集合框架的工具类。里面定义的都是静态方法。


CollectionsCollection有什么区别?

Collection是集合框架中的一个顶层接口,它里面定义了单列集合的共性方法。

它有两个常用的子接口,

List:对元素都有定义索引。有序的。可以重复元素。

Set:不可以重复元素。无序。


Collections是集合框架中的一个工具类。该类中的方法都是静态的

提供的方法中有可以对list集合进行排序,二分查找等方法。

通常常用的集合都是线程不安全的。因为要提高效率。

如果多线程操作这些集合时,可以通过该工具类中的同步方法,将线程不安全的集合,转换成安全的。


1,指定类型的数组到底要定义多长呢?

当指定类型的数组长度小于了集合的size,那么该方法内部会创建一个新的数组。长度为集合的size

当指定类型的数组长度大于了集合的size,就不会新创建了数组。而是使用传递进来的数组。

所以创建一个刚刚好的数组最优。


2,为什么要将集合变数组?

为了限定对元素的操作。不需要进行增删了。


 Arrays

Arrays

工具类,提供了对数组的常用操作

将数组转成List集合

对数组进行排序

对数组进行二分查找

将数组转为字符串显示形式


Arrays:用于操作数组的工具类。

里面都是静态方法。

asList:将数组变成list集合


把数组变成list集合有什么好处?

可以使用集合的思想和方法来操作数组中的元素。


注意:将数组变成集合,不可以使用集合的增删方法。

因为数组的长度是固定。

contains

get

indexOf()

subList();


如果你增删。那么会反生UnsupportedOperationException,


如果数组中的元素都是对象。那么变成集合时,数组中的元素就直接转成集合中的元素。

如果数组中的元素都是基本数据类型,那么会将该数组作为集合中的元素存在。


JDK1.5新特性

 增强For循环

高级for循环


格式:

for(数据类型 变量名 : 被遍历的集合(Collection)或者数组)

{

}


对集合进行遍历。

只能获取集合元素。但是不能对集合进行操作。


迭代器除了遍历,还可以进行remove集合中元素的动作。

如果是用ListIterator,还可以在遍历过程中对集合进行增删改查的动作。


传统for和高级for有什么区别呢?

高级for有一个局限性。必须有被遍历的目标。

建议在遍历数组的时候,还是希望是用传统for。因为传统for可以定义脚标。


 可变参数

方法的可变参数。

在使用时注意:可变参数一定要定义在参数列表最后面。

可变参数。

其实就是上一种数组参数的简写形式。

不用每一次都手动的建立数组对象。

只要将要操作的元素作为参数传递即可。

隐式将这些参数封装成了数组。

 静态导入

StaticImport  静态导入。


当类名重名时,需要指定具体的包名。

当方法重名是,指定具备所属的对象或者类。



import java.util.*;

import static  java.util.Arrays.*;//导入的是Arrays这个类中的所有静态成员。

import static java.util.Collections.*;


import static  java.lang.System.*;//导入了System类中所有静态成员。


其它工具类

 System

System

静态属性in为标准输入流,属于InputStream类型,read方法返回一个字节

静态属性out为标准打印流,属于PrintStream类型,print方法打印字符

可以用set方法修改属性inout

System.exit()方法退出Java虚拟机

System.gc()垃圾回收

System.getProperties()方法获得系统属性


因为PropertiesHashtable的子类,也就是Map集合的一个子类对象。

那么可以通过map的方法取出该集合中的元素。

该集合中存储都是字符串。没有泛型定义。


可不可以在jvm启动时,动态加载一些属性信息呢?


java –D<NAME>=<VALUE> xxxDemo

eg.

java –Dhh=tt HelloWorld


 Runtime

Runtime

表示系统运行时状态

exec方法执行命令 


Runtime对象

该类并没有提供构造函数。

说明不可以new对象。那么会直接想到该类中的方法都是静态的。

发现该类中还有非静态方法。

说明该类肯定会提供了方法获取本类对象。而且该方法是静态的,并返回值类型是本类类型。


由这个特点可以看出该类使用了单例设计模式完成。


该方式是static Runtime getRuntime();


 时间类

 Date

Date

使用new Date()创建时间对象代表当前系统时间

需要使用DateFormat类来进行格式化,才能显示想符合习惯的格式


 Calendar

Calendar

使用该类对时间进行操作比较方便

通过常量来表示时间的各种值,如一年中的某一天,一个月的某一天等

将对应的常量作为形参来调用相应的getaddset方法来操作对象

练习

计算出某一年的二月份有多少天?

设计一个方法可以计算工作时间,接收一个参数(工作日),方法打印出哪天完工。

 Math—Random

static double ceil(double d) 返回大于d的最小整数 注意 d = 12.3时,结果:13 

 d = -12.3 结果:-12

static double floor(double a) 返回小于a的最大整数,

static long round(double a) 返回a的四舍五入的整数


Math.random()Random.NextInt(int n)的使用;


IO

IO概述

IO流用来处理设备之间的数据传输

Java对数据的操作是通过流的方式

Java用于操作流的对象都在IO包中

流按操作对象分为两种:字节流与字符流。  字节流可以操作任何数据,字符流只能操作纯字符数据比较方便。

流按流向分为:输入流,输出流。


IO流常用基类

字节流的抽象基类:

InputStream OutputStream

字符流的抽象基类:

Reader Writer

由这四个类派生出来的子类名称都是以其父类名作为子类名的后缀。

如:InputStream的子类FileInputStream

如:Reader的子类FileReader

InputStreamReaderReader的子类


字符流的操作

读取文件

定义字符流关联指定文件

FileReader reader = new FileReader("Test.txt");

读取一个字符,返回int,该字符的码表值

int ch = reader.read();

关闭流,释放资源

reader.close();

写出文件

定义字符输出流关联指定文件

FileWriter writer = new FileWriter("Test.txt");

写出一个字符,接收int码表值

writer.write(97);

关闭流,释放资源

writer.close();

注意事项

文件路径

定义文件路径时Windows中的目录符号为“\”,但这个符号在Java中是特殊字符,需要转义。

可以用“\\”“/”表示。

读取文件

读取文件时必须保证文件存在,否则将抛出FileNotFoundException

写出文件

写出时文件如不存在时程序会创建新文件,如文件已存在则会清空原文件内容重新写入。

如需追加内容可调用FileWriter构造函数 HYPERLINK "mk:@MSITStore:D:\\ItCast\\doc\\J2SE6.0_CN.chm::/java/io/FileWriter.html" \l "FileWriter(java.lang.String,%20boolean)" FileWriter( HYPERLINK "mk:@MSITStore:D:\\ItCast\\doc\\J2SE6.0_CN.chm::/java/lang/String.html" \o "java.lang 中的类" String fileName, boolean append)

练习

拷贝一个文件

字符流待缓冲区的读写操作

自定义缓冲区读写

为什么定义缓冲区

由于单个字符读写需要频繁操作文件,所以效率非常低。

我们可以定义缓冲区将要读取或写出的数据缓存,减少操作文件次数。

缓冲区读取

先定义一个数组,然后调用FileReader读取一个数组的方法。

int read(char[] cbuf)

缓冲区写出

将要写出的数据存放在数组中,调用FileWriter方法,一次写出一个数组。

void write(char[] cbuf, int off, int len)

内置缓冲区的BufferedReaderBufferedWriter

Java提供了带缓冲功能的ReaderWriter类:BufferedReaderBufferedWriter

这两个类都是提供包装功能,需要提供其他流来使用,给其他流增加缓冲功能

当我们调用BufferedReader读取数据时,程序会从文件中一次读取8192个字符用来缓冲

当我们调用BufferedWriter写出数据时,程序会先将数据写出到缓冲数组,直到写满8192个才一次性刷出到文件

特有方法 newLine(),  readLine()

注意:BufferedReader 读取时 不会读取换行符,所以写入时会出现乱序,

在写入后加newLine();可以解决


Eg1.

需求:在硬盘上,创建一个文件并写入一些文字数据。


找到一个专门用于操作文件的Writer子类对象。FileWriter  后缀名是父类名。 前缀名是该流对象的功能。

*/

import java.io.*;

class  FileWriterDemo

{

public static void main(String[] args) throws IOException

{

//创建一个FileWriter对象。该对象一被初始化就必须要明确被操作的文件。

//而且该文件会被创建到指定目录下。如果该目录下已有同名文件,将被覆盖。

//其实该步就是在明确数据要存放的目的地。

FileWriter fw = new FileWriter("demo.txt");


//调用write方法,将字符串写入到流中。

fw.write("abcde");


//刷新流对象中的缓冲中的数据。

//将数据刷到目的地中。

//fw.flush();



//关闭流资源,但是关闭之前会刷新一次内部的缓冲中的数据。

//将数据刷到目的地中。

//flush区别:flush刷新后,流可以继续使用,close刷新后,会将流关闭。

fw.close();

}

}

Eg2.

/*

演示对已有文件的数据续写。

*/

import java.io.*;

class  FileWriterDemo3

{

public static void main(String[] args) throws IOException

{


//传递一个true参数,代表不覆盖已有的文件。并在已有文件的末尾处进行数据续写。

FileWriter fw = new FileWriter("demo.txt",true);


fw.write("nihao\r\nxiexie");


fw.close();

}

}

Eg.3

//C盘一个文本文件复制到D盘。


/*

复制的原理:

其实就是将C盘下的文件数据存储到D盘的一个文件中。


步骤:

1,在D盘创建一个文件。用于存储C盘文件中的数据。

2,定义读取流和C盘文件关联。

3,通过不断的读写完成数据存储。

4,关闭资源。

*/

import java.io.*;


class CopyText 

{

public static void main(String[] args) throws IOException

{

copy_2();

}



public static void copy_2()

{

FileWriter fw = null;

FileReader fr = null;

try

{

fw = new FileWriter("SystemDemo_copy.txt");

fr = new FileReader("SystemDemo.java");


char[] buf = new char[1024];


int len = 0;

while((len=fr.read(buf))!=-1)

{

fw.write(buf,0,len);

}

}

catch (IOException e)

{

throw new RuntimeException("读写失败");


}

finally

{

if(fr!=null)

try

{

fr.close();

}

catch (IOException e)

{

}

if(fw!=null)

try

{

fw.close();

}

catch (IOException e)

{

}

}

}


//C盘读一个字符,就往D盘写一个字符。

public static void copy_1()throws IOException

{

//创建目的地。

FileWriter fw = new FileWriter("RuntimeDemo_copy.txt");


//与已有文件关联。

FileReader fr = new FileReader("RuntimeDemo.java");


int ch = 0;


while((ch=fr.read())!=-1)

{

fw.write(ch);

}

fw.close();

fr.close();


}

}



装饰设计模式

什么情况下使用装饰设计模式

当我们需要对一个类的功能进行改进、增强的时候

装饰模式的基本格式。

含有被装饰类的引用

通过构造函数传入被装饰类对象

和被装饰类含有同样的方法,其中调用被装饰类的方法,对其进行改进、增强

和被装饰类继承同一个类或实现同一个接口,可以当做被装饰类来使用

了解BufferedReaderBufferedWriter的原理。

BufferedReaderBufferedWriter都是装饰类,他们可以装饰一个ReaderWriter,给被装饰的ReaderWriter提供缓冲的功能。

就像我们用BufferedReaderBufferedWriter装饰FileReaderFileWriter,使用的读写功能还是FileReaderFileWriter的,但给这两个类的读写添加了缓冲功能。

练习

模拟一个BufferedReader类。

模拟一个LineNumberReader类。


装饰设计模式与继承的比较

MyReader//专门用于读取数据的类。

|--MyTextReader

|--MyBufferTextReader

|--MyMediaReader

|--MyBufferMediaReader

|--MyDataReader

|--MyBufferDataReader


class MyBufferReader

{

MyBufferReader(MyTextReader text)

{}

MyBufferReader(MyMediaReader media)

{}

}

上面这个类扩展性很差。

找到其参数的共同类型。通过多态的形式。可以提高扩展性。


class MyBufferReader extends MyReader

{

private MyReader r;

MyBufferReader(MyReader r)

{}

}



MyReader//专门用于读取数据的类。

|--MyTextReader

|--MyMediaReader

|--MyDataReader

|--MyBufferReader



以前是通过继承将每一个子类都具备缓冲功能。

那么继承体系会复杂,并不利于扩展。


现在优化思想。单独描述一下缓冲内容。

将需要被缓冲的对象。传递进来。也就是,谁需要被缓冲,谁就作为参数传递给缓冲区。

这样继承体系就变得很简单。优化了体系结构。


装饰模式比继承要灵活。避免了继承体系臃肿。

而且降低了类于类之间的关系。


装饰类因为增强已有对象,具备的功能和已有的是相同的,只不过提供了更强功能。

所以装饰类和被装饰类通常是都属于一个体系中的。

字节流


基本操作与字符流相同

字节流可以操作任意类型数据

练习:拷贝一个Jpg文件


自定义缓冲区读写

原理和字符流相同,都是为了提高效率

定义数组缓冲数据,一次读取一个数组,一次写出一个数组,减少操作文件的次数

BufferedInputStreamBufferedOutputStream

BufferedReaderBufferedWriter原理相同,都是包装类

BufferedInputStreamBufferedOutputStream包装InputStreamOutputStream提供缓冲功能


转换流


字符流与字节流之间的桥梁

方便了字符流与字节流之间的操作

字节流中的数据都是字符时,转成字符流操作更高效

练习:转换System.in

注:转换流的输出流可以指定编码表


InputStreamReader,OutputStreamWriter


System类的setIn(),setout()方法可以改变输入输出设备


标准输入,输出

System类中的成员变量:inout

它们各代表了系统标准的输入和输出设备。

默认输入设备是键盘,输出设备是显示器。

System.in的类型是InputStream.

System.out的类型是PrintStreamOutputStream的子类FilterOutputStream 的子类.

练习:通过修改标准输入输出流,使用System.inSystem.out拷贝文件



流操作小结



 SHAPE  \* MERGEFORMAT 




 SHAPE  \* MERGEFORMAT 


1,

源:键盘录入。

目的:控制台。


2,需求:想把键盘录入的数据存储到一个文件中。

源:键盘。

目的:文件。


3,需求:想要将一个文件的数据打印在控制台上。

源:文件。

目的:控制台。


流操作的基本规律:

最痛苦的就是流对象有很多,不知道该用哪一个。


通过三个明确来完成。


1,明确源和目的。

源:输入流。InputStream  Reader

目的:输出流。OutputStream  Writer

2,操作的数据是否是纯文本。

是:字符流。

不是:字节流。


3,当体系明确后,在明确要使用哪个具体的对象。

通过设备来进行区分:

源设备:内存,硬盘。键盘

目的设备:内存,硬盘,控制台。


Eg.

1,将一个文本文件中数据存储到另一个文件中。复制文件。

源:因为是源,所以使用读取流。InputStream Reader 

是不是操作文本文件。

是!这时就可以选择Reader

这样体系就明确了。


接下来明确要使用该体系中的哪个对象。

明确设备:硬盘。上一个文件。

Reader体系中可以操作文件的对象是 FileReader


是否需要提高效率:是!。加入Reader体系中缓冲区 BufferedReader.



FileReader fr = new FileReader("a.txt");

BufferedReader bufr = new BufferedReader(fr);



目的:OutputStream Writer

是否是纯文本。

是!Writer

设备:硬盘,一个文件。

Writer体系中可以操作文件的对象FileWriter

是否需要提高效率:是!。加入Writer体系中缓冲区 BufferedWriter

FileWriter fw = new FileWriter("b.txt");

BufferedWriter bufw = new BufferedWriter(fw);



练习:将一个图片文件中数据存储到另一个文件中。复制文件。要按照以上格式自己完成三个明确。



---------------------------------------


2,需求:将键盘录入的数据保存到一个文件中。

这个需求中有源和目的都存在。

那么分别分析

源:InputStream Reader

是不是纯文本?是!Reader

设备:键盘。对应的对象是System.in.

不是选择Reader吗?System.in对应的不是字节流吗?

为了操作键盘的文本数据方便。转成字符流按照字符串操作是最方便的。

所以既然明确了Reader,那么就将System.in转换成Reader

用了Reader体系中转换流,InputStreamReader


InputStreamReader isr = new InputStreamReader(System.in);


需要提高效率吗?需要!BufferedReader

BufferedReader bufr = new BufferedReader(isr);


目的:OutputStream  Writer

是否是存文本?是!Writer

设备:硬盘。一个文件。使用 FileWriter

FileWriter fw = new FileWriter("c.txt");

需要提高效率吗?需要。

BufferedWriter bufw = new BufferedWriter(fw);



**************

扩展一下,想要把录入的数据按照指定的编码表(utf-8),将数据存到文件中。

目的:OutputStream  Writer

是否是存文本?是!Writer

设备:硬盘。一个文件。使用 FileWriter

但是FileWriter是使用的默认编码表。GBK.

但是存储时,需要加入指定编码表utf-8。而指定的编码表只有转换流可以指定。

所以要使用的对象是OutputStreamWriter

而该转换流对象要接收一个字节输出流。而且还可以操作的文件的字节输出流。FileOutputStream


OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("d.txt"),"UTF-8");


需要高效吗?需要。

BufferedWriter bufw = new BufferedWriter(osw);


所以,记住。转换流什么使用。字符和字节之间的桥梁,通常,涉及到字符编码转换时,

需要用到转换流。



练习:将一个文本数据打印在控制台上。要按照以上格式自己完成三个明确。


File

File

用来将文件或者文件夹路径封装成对象

方便对文件与文件夹进行操作。

File对象可以作为参数传递给流的构造函数。

了解File类中的常用方法。


File类常见方法:

1,创建

boolean createNewFile():在指定位置创建文件,如果该文件已经存在,则不创建,返回false

和输出流不一样,输出流对象一建立创建文件。而且文件已经存在,会覆盖。


boolean mkdir():创建文件夹。

boolean mkdirs():创建多级文件夹。

2,删除。

boolean delete():删除失败返回false。如果文件正在被使用,则删除不了返回falsel

void deleteOnExit();在程序退出时删除指定文件。



3,判断。

boolean exists() :文件是否存在.

isFile():

isDirectory();

isHidden();

isAbsolute();


4,获取信息。

getName():

getPath():

getParent():


getAbsolutePath() 

long lastModified() 

long length()



递归

函数自己调用自己。

注意:递归时一定要明确结束条件。

应用场景:

当某一功能要重复使用时。

练习:

列出一个文件夹下所有的子文件夹以及子文件

思考:

删除一个目录的过程是如何进行的?

复制一个目录的过程呢?


Properties

PropertiesHashtable的子类。

也就是说它具备map集合的特点。而且它里面存储的键值对都是字符串。


是集合中和IO技术相结合的集合容器。


该对象的特点:可以用于键值对形式的配置文件。


那么在加载数据时,需要数据有固定格式:键=值。


练习:限制程序运行次数。当运行次数到达5次时,给出,请您注册的提示。并不再让该程序执行。


用于记录应用程序运行次数。

如果使用次数已到,那么给出注册提示。


很容易想到的是:计数器。

可是该计数器定义在程序中,随着程序的运行而在内存中存在,并进行自增。

可是随着该应用程序的退出,该计数器也在内存中消失了。


下一次在启动该程序,又重新开始从0计数。

这样不是我们想要的。


程序即使结束,该计数器的值也存在。

下次程序启动在会先加载该计数器的值并加1后在重新存储起来。


所以要建立一个配置文件。用于记录该软件的使用次数。


该配置文件使用键值对的形式。

这样便于阅读数据,并操作数据。


键值对数据是map集合。

数据是以文件形式存储,使用io技术。

那么map+io -->properties.


配置文件可以实现应用程序数据的共享。


打印流

打印流:

该流提供了打印方法,可以将各种数据类型的数据都原样打印。


字节打印流:

PrintStream

构造函数可以接收的参数类型:

1file对象。File

2,字符串路径。String

3,字节输出流。OutputStream


字符打印流

PrintWriter

构造函数可以接收的参数类型:

1file对象。File

2,字符串路径。String

3,字节输出流。OutputStream

4,字符输出流,Writer


流的合并与切割

序列流

SequenceInputStream

可以将多个字节输入流整合成一个流,在使用这个流读取的时候,读到第一个流的末尾时继续读第二个,第二个读到末尾则继续读第三个,以此类推,直到读到最后一个流的末尾返回-1


import java.io.*;

import java.util.*;



/**

实现文件的切割与合并

@author yangmeicheng

*/

public class SplitStream 

{

public static void main(String[] args) 

{

//File file = new File("F:"+File.separator+"歌曲"+File.separator+"周杰伦 - 夜曲.ape");

//splitFile(file);

mergeFile();

}



public static void mergeFile()

{

ArrayList<FileInputStream> al = new ArrayList<FileInputStream>();

FileInputStream fis = null;

for (int i=1;i<=7 ;i++ )

{

try

{

fis = new FileInputStream("D:"+File.separator+i+".part");

}

catch (FileNotFoundException e)

{

e.printStackTrace();

}

al.add(fis);

}


final Iterator<FileInputStream> it = al.iterator();


Enumeration<FileInputStream> en = new Enumeration<FileInputStream>()

{

public boolean hasMoreElements()

{

return it.hasNext();

}


public FileInputStream nextElement()

{

return it.next();

}

};


SequenceInputStream sis = null;

BufferedOutputStream bos = null;


try

{

sis = new SequenceInputStream(en);

bos = new BufferedOutputStream(new FileOutputStream("d:\\yequ.mp3"));


int num = 0;

byte [] buff = new byte[1024*1024];


while ((num = sis.read(buff)) != -1)

{

bos.write(buff,0,num);

}

}

catch (IOException e)

{

throw new RuntimeException("合并失败");

}

finally 

{

if (null != sis )

{

try

{

sis.close();

}

catch (IOException e)

{

throw new RuntimeException("合并流读取失败");

}

}


if (null != bos )

{

try

{

bos.close();

}

catch (IOException e)

{

throw new RuntimeException("合并流写入失败");

}

}

}



}


public static void splitFile(File file)

{

BufferedInputStream bis = null;

BufferedOutputStream bos = null;


try

{

bis = new BufferedInputStream(new FileInputStream(file));

int num = 0;

int count = 1;

byte [] buff = new byte[1024*1024*4];

while ((num = bis.read(buff)) != -1)

{

bos = new BufferedOutputStream(new FileOutputStream("d:\\"+(count++)+".part"));

bos.write(buff,0,num);

}

}

catch ( IOException e)

{

throw new RuntimeException("切割失败");

}

finally 

{

if (null != bis )

{

try

{

bis.close();

}

catch (IOException e)

{

throw new RuntimeException("切割流读取失败");

}

}


if (null != bos )

{

try

{

bos.close();

}

catch (IOException e)

{

throw new RuntimeException("切割流写入失败");

}

}

}

}

}

对象序列化

ObjectOutputStream

可以将实现了Serializable的接口的对象转成字节写出到流中

ObjectInputStream

可以从流中读取一个ObjectOutputStream流写出的对象


静态成员变量不能持久化

transient 修饰 可以阻止成员被序列化


 管道流

管道流

PipedInputStream:管道输入流,可以从管道输出流中读取数据

PipedOutputStream:管道输出流,可以向管道输入流中写出数据


在建立管道输出流时可以在构造方法中将管道输入流传入,

或者用connect()方法将输入输出流接上,

建议:输出与输入在两个不同的线程进行操作


随机读写

随机访问文件

RandomAccessFile



该类不是算是IO体系中子类。

而是直接继承自Object


但是它是IO包中成员。因为它具备读和写功能。

内部封装了一个数组,而且通过指针对数组的元素进行操作。

可以通过getFilePointer获取指针位置,

同时可以通过seek改变指针的位置。



其实完成读写的原理就是内部封装了字节输入流和输出流。


通过构造函数可以看出,该类只能操作文件。

而且操作文件还有模式:只读r,,读写rw等。


如果模式为只读 r。不会创建文件。会去读取一个已存在文件,如果该文件不存在,则会出现异常。

如果模式rw。操作的文件不存在,会自动创建。如果存则不会覆盖。


下载软件实现原理


操作基本数据类型的流

DataInputStreamDataOutputStream

可以按照基本数据类型占用空间大小读写数据


操作字节数组的流

ByteArrayOutputStream 写出到字节数组(内存)中,可以获取写出的内容装入一个字节数组。通常我们用这个流来缓冲数据。

ByteArrayInputStream:可以从一个字节数组中读取字节。

CharArrayWriter:写出字符到字符数组(内存)中,可以获取写出的内容装入一个字符数组。

CharArrayReader:可以从一个字符数组中读取字符。


用于操作字节数组的流对象。


ByteArrayInputStream :在构造的时候,需要接收数据源,。而且数据源是一个字节数组。


ByteArrayOutputStream 在构造的时候,不用定义数据目的,因为该对象中已经内部封装了可变长度的字节数组。

这就是数据目的地。


因为这两个流对象都操作的数组,并没有使用系统资源。

所以,不用进行close关闭。



在流操作规律讲解时:


源设备,

键盘 System.in,硬盘 FileStream,内存 ArrayStream

目的设备:

控制台 System.out,硬盘FileStream,内存 ArrayStream



用流的读写思想来操作数据。


字符编码

码表

ASCII:英文码表,每个字符占1个字节。A65a97

GB2312:兼容ASCII,包含中文,每个英文占1个字节(正数),中文占2个字节(2个负数)。 

GBK:兼容GB2312,包含更多中文,每个英文占1个字节(正数),中文占2个字节(第一个负数、第二个可正可负)。 

Unicode:国际码表,每个字符占2个字节。Java中存储字符类型就是使用的Unicode编码。

UTF-8:国际码表,英文占1个字节,中文占3个字节。


GUI(图形用户界面)

GUI概念

什么是GUI

GUIGraphical User Interface的缩写,图形化用户界面

awtswing

JavaGUI提供的对象都存在java.awtjavax.swing两个包中

awt依赖于本地系统平台,如颜色样式显示

swing跨平台

组件与容器

组件 Component,是GUI图形界面的组成单元。

容器Container,可以存放组件,也可以存放容器。


组件关系继承图


布局管理

FlowLayout(流式布局管理器)

从左到右的顺序排列。

BorderLayout(边界布局管理器)

东,南,西,北,中

GridLayout(网格布局管理器)

规则的矩阵

CardLayout(卡片布局管理器)

选项卡

GridBagLayout(网格包布局管理器)

非规则的矩阵


建立一个窗体

窗体中可以存放各种组件,所以窗体是容器Container。创建时我们使用的是它的子类

Container的常用子类有两个,WindowPanelWindow是我们常用的窗体,Panel是用来布局的不可见的。

Window也有两个常用子类,FrameDialogFrame是我们常用的带有标题和边框的顶层窗口,Dialog是对话框。

所有AWT包中的类都会运行在AWT线程上

事件处理

事件处理机制

事件:用户对组件的一个操作。

事件源:发生事件的组件。

监听器:我们需要处理某个事件,就需要在发生事件的组件上添加监听器,也就是java.awt.event包中XxxListener接口的子类。

事件处理器:监听器中的方法。监听器被添加在组件上之后,组件上发生了对应事件就会执行指定方法。


事件监听流程



常用事件分类


窗体事件,WindowEvent,窗体打开、关闭、正在关闭、激活、最小化等。

鼠标事件,MouseEvent,鼠标按下、抬起、进入、移出等。

键盘事件,KeyEvent,键盘按下、抬起等。

动作事件,ActionEvent,在某一组件上发生了定义好的动作,例如按钮上鼠标点击或按空格,菜单上鼠标点击或按回车等。



创建图形化界面:

1,创建frame窗体。

2,对窗体进行基本设置。

比如大小,位置,布局。

3,定义组件。

4,将组件通过窗体的add方法添加到窗体中。

5,让窗体显示,通过setVisible(true)


事件监听机制的特点:

1,事件源。

2,事件。

3,监听器。

4,事件处理。


事件源:就是awt包或者swing包中的那些图形界面组件。

事件:每一个事件源都有自己特有的对应事件和共性事件。


监听器:将可以触发某一个事件的动作(不只一个动作)都已经封装到了监听器中。


以上三者,在java中都已经定义好了。

直接获取其对象来用就可以了。


我们要做的事情是,就是对产生的动作进行处理。


Socket网络编程

网络编程概述

IP地址(InetAddress类)

每台网络终端在网络中都有一个独立的地址,我们在网络中传输数据就是使用这个地址。 

ipconfig:查看本机IP

ping:测试连接

本地回路地址:127.0.0.1

IPv44个字节组成,40-255。大概42亿,30亿都在北美,亚洲4亿。已经用尽。  

IPv68组,每组416进制数。

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

网络协议

HYPERLINK "http://baike.baidu.com/view/25482.htm" \t "_blank" 计算机网络中进行数据交换而建立的规则、标准或约定的集合。

UDP

面向无连接,数据不安全,速度快。不区分客户端与服务端。

TCP

面向连接(三次握手),数据安全,速度略低。分为客户端和服务端。

Socket

通信的两端都有Socket

网络通信其实就是Socket间的通信。

数据在两个Socket间通过IO传输。

Socket在应用程序中创建,通过一种绑定机制与驱动程序建立关系,告诉自己所对应的IPport


网络模型



UDP传输

发送

创建DatagramSocket

创建DatagramPacket

使用DatagramSocket发送DatagramPacket

关闭DatagramSocket

接收

创建DatagramSocket

创建DatagramPacket

使用DatagramSocket接收DatagramPacket

关闭DatagramSocket

TCP传输

客户端

创建Socket连接服务端

调用SocketgetInputStream()getOutputStream()方法获取和服务端相连的管道流

输入流可以读取服务端输出流写出的数据

输出流可以写出数据到服务端的输入流

服务端

创建ServerSocket

调用ServerSocketaccept()方法接收一个客户端请求,得到一个Socket

调用SocketgetInputStream()getOutputStream()方法获取和客户端相连的管道流

输入流可以读取客户端输出流写出的数据

输出流可以写出数据到客户端的输入流

URL-URLConnection

day24

正则表达式


正则表达式:符合一定规则的表达式。

作用:用于专门操作字符串。

特点:用于一些特定的符号来表示一些代码操作。这样就简化书写。

所以学习正则表达式,就是在学习一些特殊符号的使用。


好处:可以简化对字符串的复杂操作。

弊端:符号定义越多,正则越长,阅读性越差。



具体操作功能:


1,匹配:String  matches方法。用规则匹配整个字符串,只要有一处不符合规则,就匹配结束,返回false

2,切割:String split();


3,替换:String replaceAll(regex,str);如果regex中有定义组,可以在第二参数中通过$符号获取正则表达式中的已有的组。


4,获取:将字符串中的符合规则的子串取出。


操作步骤:

1,将正则表达式封装成对象。

2,让正则对象和要操作的字符串相关联。

3,关联后,获取正则匹配引擎。

4,通过引擎对符合规则的子串进行操作,比如取出。


正则表达式的构造摘要 

构造

匹配

 


字符

x

字符 x

\\


反斜线字符

\0n带有八进制值 0 的字符 n (0 <= n <= 7)

\0nn带有八进制值 0 的字符 nn (0 <= n <= 7)

\0mnn带有八进制值 0 的字符 mnn<= m <= 3<= n <= 7

\xhh带有十六进制值 0x 的字符 hh

\uhhhh带有十六进制值 0x 的字符 hhhh

\t制表符 ('\u0009')

\n新行(换行)符 ('\u000A')

\r回车符 ('\u000D')

\f换页符 ('\u000C')

\a报警 (bell) ('\u0007')

\e转义符 ('\u001B')

\cx


对应于 x 的控制符

 


字符类

[abc]ab c(简单类)

[^abc]任何字符,除了 ab c(否定)

[a-zA-Z]a z A Z,两头的字母包括在内(范围)

[a-d[m-p]]a d m p[a-dm-p](并集)

[a-z&&[def]]de f(交集)

[a-z&&[^bc]]a z,除了 b c[ad-z](减去)

[a-z&&[^m-p]]a z,而非 m p[a-lq-z](减去)

 


预定义字符类

.任何字符(与 HYPERLINK "mk:@MSITStore:E:\\桌面\\api\\JDK_API_1_6_zh_CN.CHM::/java/util/regex/Pattern.html" \l "lt#lt" 行结束符可能匹配也可能不匹配)

\d数字:[0-9]

\D非数字: [^0-9]

\s空白字符:[ \t\n\x0B\f\r]

\S非空白字符:[^\s]

\w单词字符:[a-zA-Z_0-9]

\W非单词字符:[^\w]

 


POSIX 字符类(仅 US-ASCII

\p{Lower}小写字母字符:[a-z]

\p{Upper}大写字母字符:[A-Z]

\p{ASCII}所有 ASCII[\x00-\x7F]

\p{Alpha}字母字符:[\p{Lower}\p{Upper}]

\p{Digit}十进制数字:[0-9]

\p{Alnum}字母数字字符:[\p{Alpha}\p{Digit}]

\p{Punct}标点符号:!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~

\p{Graph}可见字符:[\p{Alnum}\p{Punct}]

\p{Print}可打印字符:[\p{Graph}\x20]

\p{Blank}空格或制表符:[ \t]

\p{Cntrl}控制字符:[\x00-\x1F\x7F]

\p{XDigit}十六进制数字:[0-9a-fA-F]

\p{Space}空白字符:[ \t\n\x0B\f\r]

 


java.lang.Character 类(简单的  HYPERLINK "mk:@MSITStore:E:\\桌面\\api\\JDK_API_1_6_zh_CN.CHM::/java/util/regex/Pattern.html" \l "jcc#jcc" java 字符类型

\p{javaLowerCase}


等效于 java.lang.Character.isLowerCase()

\p{javaUpperCase}


等效于 java.lang.Character.isUpperCase()

\p{javaWhitespace}


等效于 java.lang.Character.isWhitespace()

\p{javaMirrored}


等效于 java.lang.Character.isMirrored()

 


Unicode 块和类别的类

\p{InGreek}Greek 块(简单 HYPERLINK "mk:@MSITStore:E:\\桌面\\api\\JDK_API_1_6_zh_CN.CHM::/java/util/regex/Pattern.html" \l "ubc#ubc" )中的字符

\p{Lu}大写字母(简单 HYPERLINK "mk:@MSITStore:E:\\桌面\\api\\JDK_API_1_6_zh_CN.CHM::/java/util/regex/Pattern.html" \l "ubc#ubc" 类别

\p{Sc}


货币符号

\P{InGreek}


所有字符,Greek 块中的除外(否定)

[\p{L}&&[^\p{Lu}]] 


所有字母,大写字母除外(减去)

 


边界匹配器

^


行的开头

$


行的结尾

\b


单词边界

\B


非单词边界

\A


输入的开头

\G


上一个匹配的结尾

\Z输入的结尾,仅用于最后的 HYPERLINK "mk:@MSITStore:E:\\桌面\\api\\JDK_API_1_6_zh_CN.CHM::/java/util/regex/Pattern.html" \l "lt#lt" 结束符(如果有的话)

\z


输入的结尾

 


Greedy 数量词

X?


X,一次或一次也没有

X*


X,零次或多次

X+


X,一次或多次

X{n}


X,恰好 n

X{n,}


X,至少 n

X{n,m}


X,至少 n 次,但是不超过 m

 


Reluctant 数量词

X??


X,一次或一次也没有

X*?


X,零次或多次

X+?


X,一次或多次

X{n}?


X,恰好 n

X{n,}?


X,至少 n

X{n,m}?


X,至少 n 次,但是不超过 m

 


Possessive 数量词

X?+


X,一次或一次也没有

X*+


X,零次或多次

X++


X,一次或多次

X{n}+


X,恰好 n

X{n,}+


X,至少 n

X{n,m}+


X,至少 n 次,但是不超过 m

 


Logical 运算符

XY

X 后跟 Y

X|Y


X Y

(X)X,作为 HYPERLINK "mk:@MSITStore:E:\\桌面\\api\\JDK_API_1_6_zh_CN.CHM::/java/util/regex/Pattern.html" \l "cg#cg" 捕获组

 


Back 引用

\n任何匹配的 nth  HYPERLINK "mk:@MSITStore:E:\\桌面\\api\\JDK_API_1_6_zh_CN.CHM::/java/util/regex/Pattern.html" \l "cg#cg" 捕获组

 


引用

\


Nothing,但是引用以下字符

\QNothing,但是引用所有字符,直到 \E

\ENothing,但是结束从 \Q 开始的引用

 


特殊构造(非捕获)

(?:X)


X,作为非捕获组

(?idmsux-idmsux) Nothing,但是将匹配标志 HYPERLINK "mk:@MSITStore:E:\\桌面\\api\\JDK_API_1_6_zh_CN.CHM::/java/util/regex/Pattern.html" \l "CASE_INSENSITIVE#CASE_INSENSITIVE" i  HYPERLINK "mk:@MSITStore:E:\\桌面\\api\\JDK_API_1_6_zh_CN.CHM::/java/util/regex/Pattern.html" \l "UNIX_LINES#UNIX_LINES" d  HYPERLINK "mk:@MSITStore:E:\\桌面\\api\\JDK_API_1_6_zh_CN.CHM::/java/util/regex/Pattern.html" \l "MULTILINE#MULTILINE" m  HYPERLINK "mk:@MSITStore:E:\\桌面\\api\\JDK_API_1_6_zh_CN.CHM::/java/util/regex/Pattern.html" \l "DOTALL#DOTALL" s  HYPERLINK "mk:@MSITStore:E:\\桌面\\api\\JDK_API_1_6_zh_CN.CHM::/java/util/regex/Pattern.html" \l "UNICODE_CASE#UNICODE_CASE" u  HYPERLINK "mk:@MSITStore:E:\\桌面\\api\\JDK_API_1_6_zh_CN.CHM::/java/util/regex/Pattern.html" \l "COMMENTS#COMMENTS" x on - off

(?idmsux-idmsux:X)  X,作为带有给定标志  HYPERLINK "mk:@MSITStore:E:\\桌面\\api\\JDK_API_1_6_zh_CN.CHM::/java/util/regex/Pattern.html" \l "CASE_INSENSITIVE#CASE_INSENSITIVE" i  HYPERLINK "mk:@MSITStore:E:\\桌面\\api\\JDK_API_1_6_zh_CN.CHM::/java/util/regex/Pattern.html" \l "UNIX_LINES#UNIX_LINES" d  HYPERLINK "mk:@MSITStore:E:\\桌面\\api\\JDK_API_1_6_zh_CN.CHM::/java/util/regex/Pattern.html" \l "MULTILINE#MULTILINE" m  HYPERLINK "mk:@MSITStore:E:\\桌面\\api\\JDK_API_1_6_zh_CN.CHM::/java/util/regex/Pattern.html" \l "DOTALL#DOTALL" s  HYPERLINK "mk:@MSITStore:E:\\桌面\\api\\JDK_API_1_6_zh_CN.CHM::/java/util/regex/Pattern.html" \l "UNICODE_CASE#UNICODE_CASE" u  HYPERLINK "mk:@MSITStore:E:\\桌面\\api\\JDK_API_1_6_zh_CN.CHM::/java/util/regex/Pattern.html" \l "COMMENTS#COMMENTS" x on - off HYPERLINK "mk:@MSITStore:E:\\桌面\\api\\JDK_API_1_6_zh_CN.CHM::/java/util/regex/Pattern.html" \l "cg#cg" 非捕获组 

(?=X)


X,通过零宽度的正 lookahead

(?!X)


X,通过零宽度的负 lookahead

(?<=X)


X,通过零宽度的正 lookbehind

(?<!X)


X,通过零宽度的负 lookbehind

(?>X)


X,作为独立的非捕获组


反斜线、转义和引用 

反斜线字符 ('\') 用于引用转义构造,如上表所定义的,同时还用于引用其他将被解释为非转义构造的字符。因此,表达式 \\ 与单个反斜线匹配,而 \{ 与左括号匹配。 

在不表示转义构造的任何字母字符前使用反斜线都是错误的;它们是为将来扩展正则表达式语言保留的。可以在非字母字符前使用反斜线,不管该字符是否非转义构造的一部分。 

根据  HYPERLINK "http://java.sun.com/docs/books/jls" Java Language Specification 的要求,Java 源代码的字符串中的反斜线被解释为  HYPERLINK "http://java.sun.com/docs/books/jls/third_edition/html/lexical.doc.html" \l "100850" Unicode 转义或其他 HYPERLINK "http://java.sun.com/docs/books/jls/third_edition/html/lexical.doc.html" \l "101089" 字符转义。因此必须在字符串字面值中使用两个反斜线,表示正则表达式受到保护,不被 Java 字节码编译器解释。例如,当解释为正则表达式时,字符串字面值 "\b" 与单个退格字符匹配,而 "\\b" 与单词边界匹配。字符串字面值 "hello" 是非法的,将导致编译时错误;要与字符串 (hello) 匹配,必须使用字符串字面值 "\\(hello\\)" 

字符类 

字符类可以出现在其他字符类中,并且可以包含并集运算符(隐式)和交集运算符 (&&)。并集运算符表示至少包含其某个操作数类中所有字符的类。交集运算符表示包含同时位于其两个操作数类中所有字符的类。 

字符类运算符的优先级如下所示,按从最高到最低的顺序排列: 

1    字面值转义    \x

2    分组[...]

3    范围a-z

4    并集[a-e][i-u]

5    交集[a-z&&[aeiou]]

注意,元字符的不同集合实际上位于字符类的内部,而非字符类的外部。例如,正则表达式 . 在字符类内部就失去了其特殊意义,而表达式 - 变成了形成元字符的范围。 

行结束符

行结束符 是一个或两个字符的序列,标记输入字符序列的行结尾。以下代码被识别为行结束符: 

新行(换行)符 ('\n') 

后面紧跟新行符的回车符 ("\r\n") 

单独的回车符 ('\r') 

下一行字符 ('\u0085') 

行分隔符 ('\u2028')  

段落分隔符 ('\u2029) 

如果激活  HYPERLINK "mk:@MSITStore:E:\\桌面\\api\\JDK_API_1_6_zh_CN.CHM::/java/util/regex/Pattern.html" \l "UNIX_LINES" UNIX_LINES 模式,则新行符是唯一识别的行结束符。 

如果未指定  HYPERLINK "mk:@MSITStore:E:\\桌面\\api\\JDK_API_1_6_zh_CN.CHM::/java/util/regex/Pattern.html" \l "DOTALL" DOTALL 标志,则正则表达式 . 可以与任何字符(行结束符除外)匹配。 

默认情况下,正则表达式 ^ $ 忽略行结束符,仅分别与整个输入序列的开头和结尾匹配。如果激活  HYPERLINK "mk:@MSITStore:E:\\桌面\\api\\JDK_API_1_6_zh_CN.CHM::/java/util/regex/Pattern.html" \l "MULTILINE" MULTILINE 模式,则 ^ 在输入的开头和行结束符之后(输入的结尾)才发生匹配。处于  HYPERLINK "mk:@MSITStore:E:\\桌面\\api\\JDK_API_1_6_zh_CN.CHM::/java/util/regex/Pattern.html" \l "MULTILINE" MULTILINE 模式中时,$ 仅在行结束符之前或输入序列的结尾处匹配。 

组和捕获 

捕获组可以通过从左到右计算其开括号来编号。例如,在表达式 ((A)(B(C))) 中,存在四个这样的组: 

1    ((A)(B(C)))

2    \A

3    (B(C))

4    (C)

组零始终代表整个表达式。 

之所以这样命名捕获组是因为在匹配中,保存了与这些组匹配的输入序列的每个子序列。捕获的子序列稍后可以通过 Back 引用在表达式中使用,也可以在匹配操作完成后从匹配器获取。 

与组关联的捕获输入始终是与组最近匹配的子序列。如果由于量化的缘故再次计算了组,则在第二次计算失败时将保留其以前捕获的值(如果有的话)例如,将字符串 "aba" 与表达式 (a(b)?)+ 相匹配,会将第二组设置为 "b"。在每个匹配的开头,所有捕获的输入都会被丢弃。 

(?) 开头的组是纯的非捕获 组,它不捕获文本,也不针对组合计进行计数。











Throwable


Error



Exception






子类






子类


RuntimeException






子类


 EMBED PBrush  


字符流


Reader


Writer


InputStreamReader


OutputStreamWriter


FileReader


FileWriter


BufferedReader


BufferedWriter


FileInputStream


字节流


FilterInputStream


FilterOutputStream


BufferedInputStream


BufferedOutputStream


InputStream


OutputStream


FileOutputStream




声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/码创造者/article/detail/756136
推荐阅读
  

闽ICP备14008679号