java体系:
-java基础
-java oop 重点
-java api
-java web
前端和后端
-java 框架
-互联网架构
-大量的数据
java基础:
环境搭建:
-安装jdk 有两个文件夹
jdk
-jre
-jvm
-lib rt.jar
-bin 命令 javac 和 java命令
-src.zip 源代码
jre:
-jvm java的虚拟机
-lib 类库 rt.jar
-安装eclipse
-解压即可
-不能在中文路径和空格
-启动时会自动寻找jre
-工作空间中有.metadata文件夹,存储的是所有的eclipse的配置(建议:定期清理)
-eclipse跟jre绑定,改成绑定内部jre
window-preferences-java-installed jres- add(standard vm)--选择一个jdk中的jre
-eclipse配置整个工作空间的编码
window-preferences-general-workspace-text file encoding-UTF-8
java开发环境详细介绍:
java的命名规范:
-帕斯卡命名法:所有单词的第一个字母为大写
一般情况下用于对类名的定义
-骆驼(驼峰)命名法:从第二个单词开始所有的单词第一个字母要大写,其余全小写
一般情况下,变量名,方法名用骆驼命名法
把帕斯卡和骆驼命名法合并成都叫做骆驼命名法
用eclipse创建java工程
在project explore中的空白处,右键单击选择-new-project- java project
出现一个create a java project窗口
project name:填入java 项目的名称
use defalut location:当前项目存储的位置,一般默认就是启动eclipse时指定的工作空间
jre:指定当前工程所依赖的jre类库版本
-泛指某个版本的jre
-只给当前的工程指定一个固定版本的jre
-使用当前工作空间中指定的jre版本,工作空间中的jre变换,随之本项目的jre也跟工作空间的jre同步
project layout:项目的布局
-当前项目的源代码不在src目录中,即src目录不存在
-当前项目的所有的源代码都必须放在src目录中
点击next
出现java setting窗口
source:当前项目的源代码放置的位置,即src目录
project:当前项目所依赖其他项目
libraies:当前项目所依赖的类库
点击finish 创建工程结束
创建package包,在需要创建的地方比如src文件夹上右键单击--new-package
-在java中称之为package包,在window系统中则称之为文件夹
-name:package的名称,只能是小写,一般情况下是网址的倒着写
-在java中包名之间用.符号分割,在window中是代表的是文件夹和子文件夹
比如:
网址 包名
163.com com.163
tedu.cn cn.tedu
tarena.com.cn cn.com.tarena
...
这样定义可以按模块划分
cn.tedu.module1.submodulename
cn.tedu.module2.submodulename
用clipse创建java类
右键单击package名称--new --class
出现创建java类的窗口
source folder:当前类存贮在哪个元代码的文件夹下
package:当前类放置的在哪个包中
name:类的名称,符合帕斯卡命名法
modifieres:此单词圆形modify(修改,修饰)
此处用的引申义,可以编辑,可修改的范围
-public:公有 public class A
-private:私有 private class A
-protected:保护 protected class A
-package/default/friendly:默认 clas A
-abstract:抽象 abstract class A
-final:最终 final class A
-static 静态类
super class:长辈的类,指定当前类要继承哪个长辈的类,如果不指定,默认继承自java.lang.Object
interface:指定当前类的长辈接口是哪些,
which method...指定类中需要添加哪些方法
do you want to add comment:是否给当前类添加注释
点击finish,创建类完毕
类创建完毕后,在类中写java代码,根据需求来写
运行java代码:
方式一
右件单击文件中的任意位置--run as--java application
方式二:
右键单击包中的文件名字--run as --java appliaction
方式三:
在工具栏中选择一个执行按钮
开发中的一个常用技巧:
点击eclipse窗口中的project 菜单项--clean(清理)
如果执行此项目.eclipse会把当前工程中的bin目录中的编译完的class文件删掉
然后重新编译java的源代码文件.生成class文件,存储在bin目录中
注意两个名词:
运行路径:java项目中运行路径在bin目录中,实际run as执行的是bin目录中的class文件
开发源代码路径:java中的开发路径在src中
eclipse常用的快捷键:
ctrl+c 复制
ctrl+v 粘贴
ctrl+a 全选
ctrl+z 撤销
ctrl+y 取消撤销
ctrl+x 剪切
ctrl+d 删除光标所在的行,删除所选择的内容
ctrl+/ 注释和取消注释
ctrl+shift+/ 多行注释
ctrl+f 打开查找和替换的窗口
ctrl+h 打开复杂查找窗口
ctrl+w 关闭当前文档
ctrl+shift+w 关闭所有已经打开的文档窗口
ctrl+s 保存当前文档
ctrl+shift+s 全部保存
ctrl+-> 光标右移一个单词,遇到空格就移动一个空格
shift+-> 选择一个字符
ctrl+shift+-> 右移一个单词,且选中
shift+home 从当前光标位置一直选择到行头
shift+end 从当前光标位置一直选择到行尾
home 定位光标到行头
end 定位光标到行尾
ctrl+shift+home 从当前光标位置一直选择到文件头
ctrl+shift+end 从当前光标位置一直选择到文件尾
ctrl+shift+o 导入类所需要包
ctrl+shift+f 格式化源代码
alt+shift+z 给选择代码添加模板
f2 选择后按f2是更名
在实际开发中,必须注意一个配置
java的编译等级设置:指定.java文件用什么版本来编译
方式一:统一对当前工作空间中的所有的项目做编译等级的设置
window-preferences-java-complier-jdk compliance level
方式二:对指定的项目做编译等级设置
右键单击要设置的项目名称--properties--java complier--java compliance level
注意:
编译等级的设置原则:
实际使用的jre的版本和编译等级的版本相同即可
只能降级编译,不能升级编译
应用场景,在开发中可以用户各个版本测试
在开发中项目名称有红叉,但java代码没有红叉
一定要打开,window--show view --problem标签
然后看标签中的提示
java基础语法:
java是什么:java就是一门语言
公有类的名字必须跟文件名一致,一个java文件中可以写多个类,但公有类只能有一个
每个类中都可以写main方法,说明main方法可以有很多,运行哪个main方法取决于使用者
注意:一百个写代码有一百种写法,没有最优的写法,只有适合不适合
任何一段代码的好和坏一定是有前提
操作技巧:
-双击代码的标签,标签会放大,再次双击会变小
-拖拽代码的标签,可以在eclipse中分屏显示
-右键单击代码的标签,可以有各种关闭
如何恢复eclipse的视图窗口
每种视图都很多的标签
方式一:全部恢复
window--perspective--reset prespective 重置视图
方式二:只恢复个别的标签
window--show view--选择具体的视图
导入别人的工程到当前的eclipse中
右键单击project explorer --import--import--general--existing projects into workspace
导入别人工程的时候注意在本eclipse下project--clean
任何语言做项目开发:
1.准备数据/获取数据
2.处理数据
3.显示处理完数据的结果
从现在开始,所有精力都要放在如何利用java语言来存储数据,
计算机中最小的单位是bit 比特
计算机中最小的存储单位byte 字节
一个字节由8个bit组成, 一个bit要么是0要么是1
1kbyte=1024byte
1mbyte=1024kbyte
1gb=1024mb
1tb=1024gb
1peta byte=1024tbyte
1exa byte=1024pb
1zeta byte=1024eb
1yotta byte=1024zb
java中的八种基本数据类型
byte 1个字节 用来存储字节数据
short 2个字节 用来存储短整型数据
int 4个字节 用来存储整型数据
long 8个字节 用来存储长整型的数据(可以用l或L结尾)
float 4个字节 用来存储浮点数据(可以用f或F结尾)
double8个字节 用来存储浮点数据(可以用d或D结尾)
char 2个字节 用来存储字符型数据(必须用单引号引起来) 注意java中一个汉字是一个字符
boolean 根据jdk版本的不同,占用的字节也不同
用来存储布尔类型数据(true或false)
规定8种基本数据类型的目的:
-限定数据的范围
-占用内存的实际的大小
-申请内存空间中只能放置指定类型的数据(java是强类型语言,任何数据必须指定类型)
变量的命名规范:
变量名中可以包含字母,数字,_,$,但就是不能以数字开头
用变量来存储数据:
语法:
数据类型 变量名称;
比如:
int age;//是以age为理由跟内存申请4个字节的内存空间,但空间中没有数据
语法:
数据类型 变量名称=值;
比如
int age=20;//是以age为理由跟内存申请4个字节的内存空间,但空间中有数据20
float price=100.111F;//是以price为理由跟内存申请4个字节的内存空间,但空间中有数据100.111
The local variable age may not have been initialized
本地 变量 age 没有被初始化
Syntax error on token "%", delete this token
语法 错误 关于符号 % 删除这个 符号
Duplicate local variable age
重复 本地 变量 age
运算符:
赋值运算符 =
从右往左 把等号右边的结果赋值给等号左边的变量
算术运算符 + - * / % ++ --
int i=3;
i++;//等价于i=i+1
int i=3;
//先用i值后加一
System.out.println(i++);//输出的结果是3,输出完后是4
int i=3
++i;//等价于i=i+1
int i=3;
//先加一,后用i值
System.out.println(++i);//输出的结果是4,输出完的结果是4
类型转换问题:
大容器和小容器的关系
大容器=小容器;把小容器中的数据赋值给大的容器,自然转换 ,所谓向上造型
小容器=大容器;把大容器中的数据赋值给小的容器,强制转换(慎用),所谓向下造型
逻辑运算符:
结果是boolean类型
& 与 且
| 或 或者
! 非 取反
false & false=false
false & true=false
true & false=false
true & true=true
结论:
只要有一个为假值,结果就为假值
只有两个同时为真值,结果就为真值
false | false=false
false | true=true
true | false=true
true | true=true
结论:
只要有一个为真值,结果就为真值
只有两个同时为假值,结果就为假值
&& 短路与 且
|| 短路或 或者
false && false=false
false && true=false
true && false=false
true && true=true
结论:
只要有一个为假值,结果就为假值
只有两个同时为真值,结果就为真值
false || false=false
false || true=true
true || false=true
true || true=true
结论:
只要有一个为真值,结果就为真值
只有两个同时为假值,结果就为假值
双符号和单符号的区别:结果都是一样,但是使用cpu是不一样的
双符号的特点:
&& 短路与:
双符号&&左边为假值,那么双符号右边就不进行运算
双符号&&左边为真值,那么双符号右边就进行运算
|| 短路或:
双符号||左边为真值,那么双符号右边就不进行运算
双符号||左边为假值,那么双符号右边就进行运算
在实际的开发中,单符号和双符号都可以使用,但推荐用双符号
因为这样可以减少cpu的使用率,提高cpu的运行效率
关系运算符:
结果是boolean类型
> >= < <= == !=
运算符的优先级顺序
最高()>!>算术运算符>关系运算符>&&>||> =
程序的三种基本结构:
顺序结构:代码按照顺序从上至下逐行运行
选择分支结构:基于顺序结构,根据某种条件选择性执行某些代码
循环结构:基于顺序结构,根据某种条件,重复执行某些代码
结论:
程序的整体执行时顺序结构执行
选择分支结构和循环结构,更适合做处理数据
if结构:
语法:
if(条件){
//代码块
}
说明:
if在java中是"如果"的意思
条件的结果是boolean类型数据
如果条件为真值,就执行代码块
如果条件为假值,执行if结构的右大括号后面的代码
语法:
if(条件){
//代码块1
}else{
//代码块2
}
说明:
if条件的结果为boolean
如果条件为真值,就执行代码块1
如果条件为假值,就执行代码块2
最终只能选择其中某个代码块执行
if嵌套:
if中嵌套if-else
else中嵌套if-else
if(性别nv){
//是女
if(年龄>60){
//是nv的且年龄大于60
}else{
//是女的且年龄小于60
if(体重300){
//是女小于60岁且体重300斤
if(身价过亿){
//是女小于60岁且体重300斤,身价过亿
}
}else{
//是女小于60岁且体重小于300斤
if(很丑){
//是女的小于60岁体重小于300,长很难看
}else{
if(很穷){
是女的小于60岁体重小于300,长很很好看,但很穷
}
}
}
}
}else{
//是男的
}
java的方法:
为什么需要方法:就是为了代码的重用,对于程序员来说
写一次代码,可以使用多次
有利于程序的维护
有利于程序功能的扩展
满足单一职责,每个方法只做一件事,符合此原则复用率高
提高开发效率
结论:一个好的程序
满足:
可复用性
可维护性
可扩展性
方法的语法:
方法的返回数据类型 方法的名称(参数列表){
//方法体的代码块
return 返回数据;
}
说明:
方法的返回数据类型:可以是任意的已知的类型,
比如:八种基本数据类型,String类型
还有后续的类类型
注意:有一个特殊的类型 void
void代表无类型
方法的名称:可以由程序员自定义任何名称,但必须见名知意
必须符合骆驼命名法
比如:
public String getName();
public void setName(String name);
参数列表:必须放在小括号中,参数的个数可以是0个,也可以是多个
参数之间用逗号间隔,但也不能太多,多个的标准没有固定
的数值,定义方法的人和调用方法的人能就收可以
public int sum(int a,int b,int c,int d)
方法体:
是由若干java代码组成,能够完成某一种功能
一般情况下,方法体功能的简称就是方法的名称
如果参数列表有参数,那么就可以在方法体中使用参数的数据
但是在方法体外是不能使用方法的参数的
return 返回数据:
返回数据的类型,就是方法的返回类型,两者之间类型必须一致
返回数据只能返回䘝数据,不能返回多个数据
如果需要返回多个数据,就把多个数据打包
打包的方式:用数组的方式打包
用类来打包
返回数据可以省略,只有return关键字,那么方法的返回必须是void
类型,
如果返回类型是void,那么return关键字可以省略
如果返回类型是void,写return和不写return都可以
语法:
return 返回数据;
return;
不写return关键字
return的说明:
上面的的写法,不一定非要写在方法的最后一条语句
方法体中可以写多个return关键字,并放在方法体中任意位置
如果在方法体中出现了return关键字,并执行到此关键字
相当于结束(终止)方法的执行
如果return带有返回数据,相当于结束/终止方法的执行,同时返回一个数据
结论:return是结束方法的执行,如果有数据就返回数据
switch结构:
语法:
switch(表达式){
case 常量1:
代码块
break;
case 常量2:
代码块
break;
...
case 常量n:
代码块
break;
defualt:
代码块
break;//default放在最后,此break可以省略
}
说明:
表达式:表达式的结果类型,只能是整型数据,字符型数据,枚举类型
也可以是字符串类型,但必须注意jre的版本
jre版本1.7及以上 switch的表达式可以使用字符串类型
jre版本1.6及以下 switch的表达式是不能使用字符串类型
注意:虽然使用的jre的1.7以上,但编译等设置为1.6,同样
不能使用字符串
常量:整型,字符型,枚举,字符串
代码块:是符合常量时,需要执行的代码块
可以是多行代码,但一定不要用大括号
break:是终止的意思,如果执行到break语句,那么\
终止当前switch结构,跳出switch结构,执行后面的语句
如果没有break语句,那么找到符合的常量的时候,执行对应
代码块,但执行完代码块后,后续常量对应的代码块中的代码
一并执行,直到碰到break语句跳出switch结构
default:
如果没有任何一个case常量符合,则执行default后代码块
default块可以放在switch中任何位置,但不能放在case中
default和case是平级关系,但一般情况下default放在switch
的最后,且break可以不写
switch和if的区别:
都是分支结构
switch适合做等值判断
if结构更适合做范围判断,也能做等值判断
所有的switch结构,都可以用if来替换
不是所有的if都能用switch替代
三元运算符:
语法:
类型名 变量名=(条件表达式)? 表达示1 :表达式2;
说明:
条件表达式的结果为boolean类型
条件表达式为真值,则把表达式1的结果赋值给变量名
条件表达式为假值,则把表达式2的结果赋值给变量名
表达式1和表达式2的结果类型必须相同,且跟变量的类型相同
三元运算符的本质就是一个if-esle
比如:
int totaPage=(totalCount%pageSize==0)? (totalCount/pageSize) :(totalCount/pageSize)+1;
等价于:
if(totalCount%pageSize==0){
totalPage=totalCount/pageSize;
}else{
totalPage=totalCont/pageSize+1;
}
循环结构:循环结构一定是有规律的,没有规律的是不能用循环的
循环就是重复的执行一段代码,最好根据某种条件退出循环
循环要慎用,因为循环最浪费cpu的时间
要用循环,就尽量减少循环次数
循环的分类:
while循环,do while循环,for循环(普通for循环和增强的for循环)
while循环:
语法:
while(条件){
循环体代码块
}
说明:
条件:可以是逻辑表达式,也可以是关系表达式,
但表达式的结果一定是boolean类型
循环体代码块:
完成某一个特定功能的若干代码,
一定是由规律的
读循环或写循环循环的技巧:
要完整的推导出来前三次循环规律
如果在推导的过程中记不住步骤
建议写在纸上和电脑的记事本上
while循环的三要素:
循环变量的初值:用于记录循环次数的,达到一定的循环次数就退出循环
循环变量的终值:用判断何时退出循环
循环变量的增量或减量:没有增量或减量就是死循环
while(条件)和while(true)区别
while(条件){
//此循环是否退出,取决于条件
//一般情况下有固定的循环次数,用此循环
}
while(true){
//没有明确循环次数
//一定要配合一个if使用,根据if条件来决定何时退出循环
if(条件){
break;//终止当前循环
return://直接终止当前循环所在的方法,直接退出当前方法
}
}
重要 重要 重要
程序的调试:是程序员的必备技能,也是程序员的必杀技
做程序的目的,就是为了明确程序每一个步骤都是执行正确的
即,保证程序能够满足现实需求
程序实际执行过程中,从头执行到尾,程序员是看不到中间的
执行过程,需要在程序的中间的某个位置,设置断点,程序执行
到断点,程序就会暂停,由程序员通过某些按键逐步执行代码
以便于检查代码执行是否正确
程序调试中,重要的几个操作按键
F5:碰到函数或方法,则进入方法 step into
F6:单步执行 step over
F8:继续执行,恢复执行, resume
F8恢复执行,如果之后还有断点则会停到断点上,
如果之后没有断点则程序执行到最后
debug调试程序的技巧:就是设置断点的技巧
首先,先判断可能会出现问题的代码部分,把断点放在
可能出现问题代码前面
其次,根据错误提示,在console控制台中查看出现的错误提示
然后错误提示,设置断点
如果上面两种都不符合,恭喜你,需要把断点设置到程序的第一条语句
如果程序执行到断点,程序暂停,需要按F5或F6或F8
最终,会找到程序的错误的地方,
即使程序没有错误,也可以通过断点,逐行执行,来更好的读代码
没有写代码的高手,只有调试程序的高手
do while(条件); 循环侧重是至少执行一次循环,然后才判断条件
while(条件),侧重先判断条件,条件成立才执行循环体
for循环:
语法:
for(数据的初始化;条件;循环变量的增量或减量){
//循环体代码块
}
说明:
数据的初始化:可以定义多个变量,中间用逗号间隔,
但类型必须是同一种类型
且在第一次循环的时候执行一次
条件表达式:
可以是关系表达式,也可以是逻辑表达式,总之结果是boolean类型
且表达式有且只有一个
条件为真值则进入循环体
条件为假值则退出循环
循环变量的增量或减量:
当循环条件为真值,执行循环体
然后执行循环变量的增量或减量
然后判断条件表达式是否为真
如果为真,则继续循环体
然后还是执行循环变量的增量或减量
如此往复
直到循环条件为假,则退出循环
执行for循环后面的语句
break:终止
break用在switch机构,用于退出或终止switch结构
继续执行switch结构后面的语句
break用在循环(while,do while,for)
用户退出当前循环,继续执行当前循环结构后面的语句
break能够改变程序的执行流程
continue:继续
continue:只能用循环中,用于结束当前的循环,继续执行下一次循环
循环嵌套:
循环里套循环
外层循环一次,内层循环多次
建议不要超过3层循环
读或写循环,一定要针对最外层循环认真推导3个循环
Unreachable code 就是指定的代码是永远无法执行的
数组:
由于单个变量只能存储一个数据,如果数据量多了,起变量名
变得麻烦
用数组可以存储很多相同数据类型的数据
数据结构:按照什么样的结构来组织数据
为什么非要按照数据结构来组织数据,因为cpu使用这些数据的
时候能够更好的存储,读取,操作数据
数据结构的分类:
物理数据结构:数据在内存中的或外存中的真实的存储结构
不需要程序员关注
逻辑数据结构:就是人们按照某种想象的逻辑来组织数据的结构
java开发人员更多关注逻辑结构
jvm能够识别逻辑结构
数组就是人们想象出来的一种逻辑结构
这种逻辑结构要存储到内存中,
由jvm来控制,程序员只需要关注数据的逻辑结构用法即可
数组的的特点:
数组是一组数据的集合体
-必须是同一类型的多个数据(具体个数由程序员根据需求定义)
-在内存中必须是连续的空间,(由jvm来控制)
数组的优缺点:
优点:存储同一类型的多个数据
缺点:不能存储不同数据类型的数据,
数组申请完内存空间后,就不能动态增长空间
(解决方案:重新申请一个更大的新的数组空间,
把原来数据中的数据复制到新的数组空间中)
数组的使用:
1.定义数组(声明数组)
语法:
数据类型[] 数组的名称;
或者
数据类型 数组的名称[];//不建议
说明:
数据类型:是所有的java中的已知任何类型(八种基本数据类型,类类型)
[]:推荐放在数据类型的后面,数组名称的前面,
注意:[]可以放在数组名称的后面,但不建议
比如:
int[] aa;//不占用堆内存,但程序中可以使用,运行时报空指针异常
java.lang.NullPointerException
2.给数组申请内存空间(堆内存)
先声明数组
int[] aa;//此时没有申请堆空间
给数组申请内存空间,且给默认初值
aa=new int[5];
能使用的数组元素有5个,每个数组元素占用4个字节空间
且每个数组元素初值是0
aa[0] aa[1] aa[2] aa[3] aa[4]
申请5个空间下标索引为0--4,如果超出下标索引
会报数组下标越界异常
java.lang.ArrayIndexOutofBoundsException
3.给数据的元素赋值
如果没有明确给赋初值,则按照数据类型来给默认值
数字类型(int,float,double,short,byte,long) 默认值0
字符型默认 \u0000
对象类型 默认null
可以人为给赋值 aa[1]=100;
4.使用数组中的数据
数组名称[下标索引] 来取出数组元素的数据,并根据业务处理他
补充增强for循环:是从jdk1.5版本及以上的版本才可以使用
语法:
for(数据类型 变量名 : 数组名或集合名){
//循环体代码块
}
说明:
从数组或集合中取出第一个数据,赋值给变量名,
然后在循环体代码块中使用此变量名中的数据(变量名的作用域
仅在循环体范围内),执行完循环体,后再次从数组或集合中
取出第二个数据赋值给变量名,然后在循环体中使用变量名中的数据
以此往复,直到把数组或集合中的所有的数据都取出,循环执行完毕
注意:变量名所对应的数据类型,必须跟数组或集合中的数据类型相同
其他几种数据的声明和申请空间写法
int[] ages=new int[10];//声明数组的同时就申请了内存空间,有初值0
int[] ages={20,22,30,35,25};//声明数组的同时申请了内存空间,有初值但不是默认值
int[] ages=new int[]{20,22,30,35,25};[]里不能给数字
获取数组的长度:
int length=数组的名称.length;
比如:
int[] ages={20,22,30,35,25}
ages.length;//5
数组的排序:
排序的算法:
冒泡排序
插入排序
快速排序
等...
数组元素的排序:
在java.util.Arrays.sort(要排序的数组的名称);
会对源数组做升序排序,会改变源数组
比如:
int[] ages={20,22,30,35,25}
java.util.Arrays.sort(ages);
//20 22 25 30 35
数组的复制:
使用System.arraycopy()方法可以实现数组的复制
方法的定义:
public static void arraycopy(Object src,
int srcpos,
Object dest,
int destpos,
int length)
方法的说明:
src:源数组
srcpos:源数组中的起始位置
dest:目标数组
destpos:目标数组中的起始位置
length:要复制的数组元素的数量
比如:
int[] a=new int[]{1,2,3,4,5};
int[] a1=new int[8];
System.arraycopy(a,1,a1,0,4);
结果:
a1数组中
2,3,4,5,0,0,0,0
使用java.util.Arrays类中的copyOf方法对数组进行复制
方法的定义:
类型[] newArrayName=Arrays.copyOf(类型[] original,
int newLength);
特点:
生成的新数组是源数组的副本
newLength小于源数组,则进行截取
newLength大于源数组,则用0或null进行填充
所产生的新数组可以大于源数组的长度,属于数组扩容,
所产生的新数据可以小于源数组的长度,属于数组缩容
比如:
int[] a={1,2,3,4,5};
int[] a1=Arrays.copyOf(a,6);
结果:
a1数组中的内容:
1,2,3,4,5,0
二维数组:
语法:
数据类型[][] 二维数组的名称=new 数据类型[行数][列数];
比如:
int[][] a=new int[3][4];//声明数组并在堆空间中申请12整型空间
a[0] a[0][0] a[0][1] a[0][2] a[0][3]
a[1] a[1][0] a[1][1] a[1][2] a[1][3]
a[2] a[2][0] a[2][1] a[2][2] a[2][3]
二维数组实际上是由多个一维数组组成
实际上上面的二维数组是由 a[0] a[1] a[2]三个一维数组组成
用对象存储不同类型的数据,至少比数组的方式更进一步
面向对象:
重要的引言:
面向对象不是某个阶段就能学习精通的,但是要面向对象的基本
知识点要理解,只是用的不灵活,也会应用到某个实际项目中
无论什么样的项目,都可以用面向象+设计模式+面向对象的设计原则
可以设计出很好的程序的架构.
为什么要学习面向对象
-能够把现实世界物或事转到计算机中
如何掌握面向对象
首先要掌握类:
类:
类是一种数据结构(逻辑结构),组织多个数据类型的一种结构
数组做不到的,类能做到,对数据管理的一种进步
类中只存储数据过于单一,所以需要在类中放置方法,
由方法来操作数据
类的特点:
封装:用类把数据和操作数据的方法封装在一起
继承:类和类之间是有继承关系,是为让子类复用父类的属性或方法
多态:是面向对象的灵魂
什么是类(class):就是一个普通的名词
1.类就是一个数据结构(逻辑结构)
变量只能存储一个数据类型的数据: int i=100;
数组能存储多个相同数据类型的数据 int[] a=new int[10];
数组也是一种数据结构
类能存储多个不同类型的数据,还可以有操作数据的多个方法
比如:
public class Student{
int age;//学生的年龄
String name;//学生的名字
String stuNo;//学生的学号
public void study(){}//学生具备学习的能力
public void dajia(){}//学生具备打架的能力
}
2.类是一种特殊的由程序员自定义的数据类型
int i=3;以i为理由申请int类型大小的内存空间,存储的值是3
int[] a=new int[10];以a为理由,申请int类型的10个空间,有默认值0
public class Student{
int age;//学生的年龄
String name;//学生的名字
String stuNo;//学生的学号
public void study(){}//学生具备学习的能力
public void dajia(){}//学生具备打架的能力
}
Student stu1=new Student();以stu1为理由,申请Student类类型这么大空间
stu1里的age值是0
stu1里的name值是null
stu1里的stuNo值是null
3.有类这种数据类型,可以通过这种数据类型在堆空间中申请空间
Student stu=new Student();
可以用类类型申请无数个内存空间,且每个空间对应一个学生信息
4.基于前三点,可以把现实世界的物或事转到计算机中的类上
用类这种类型,在内存中申请内存空间,用于存储数据
使用面向对象或类,最难的事是把现实世界的物或事转换到计算机中
通俗的说法,就是把现实世界的物或事对应到计算机中的"类"上
如果了解或理解了现实世界的物或事,做成java的类就很容易
如果不了解或理解现实世界的物或事,做成java的类就很困难
实际java语法,创建一个类是很简单
public class 类名{
//最难的是在类中写属性或方法
//多个属性
//多个方法
//符合单一职责 此类只表达一件事
}
类的定义用java语法写起来很简单,难的是写什么样的属性和方法
结论:
现实世界物或事--->通过程序员的抽象成类(属性和方法)
--->用类在内存中申请(new)内存空间,并存储数据
--->从内存中取出对象的数据,根据需求处理数据
--->把处理后的结果现实在界面(控制台,网页,安卓app等...)
-----------------------------------------------------
程序员要做面向对象,
首先要考虑的是在类中放置什么样的属性和方法
类里能放:
多个属性:代表数据
多个方法:代表功能
其次要掌握对象
对象和类在jvm内存中的存储机制:
jvm把内存分为三个主要部分
方法区:存储的是类的定义,静态数据,常量池,由jvm来控制管理
堆区:数组申请的空间,对象申请的空间,总之new出来的空间
堆空间中存储的是数组数据,对象的数据,可以通过程序操作的
栈区:堆中的对象空间中存储的是属性数据,没有方法,
判断对象是否相等,主要判断的是对象中的属性的值是否相等
所以对象就是数据的载体,对象中没有方法,只有数据
通过对象打点调用方法,实际上使用的方法区中的类定义里方法
方法中使用哪个对象的数据,取决于方法是被哪个对象调用的
-----------------------------------------------------------
第一段:
为什么要使用面向对象
为什么要有类
什么是类
第二段:
类和对象在内存中的存储机制
第三段
解决数据如何存储
解决数据的安全问题
数据如何存储:
类中的属性变成私有数据
私有数据通过公有的方法来访问,因为方法可以写逻辑
公有的方法是有规律的 getter and setter
getXXX and setXXX
数据的安全性问题:
在公有方法中的逻辑,来保证私有数据正确性和安全性
---------------------------------------------------------
程序:
获取数据/准备数据
基于数据,处理
显示处理的结果
在内存中如果申请空间
1.用变量存储数据
int age=20;
2.数组:能存储一组相同数据类型的数据
3.类:能组织一批不同数据类型的数据
内存中的有了数据了
用顺序,分支,循环 可以组织很复杂的逻辑
掺杂着运算符,
有数据了,有逻辑了,可以表达现实世界的事和物
用类来存储复杂的数据,用类和类之间的关系,来表达现实世界的复杂关系
程序员能写程序能帮助人们做很多的事情
给对象存储数据的方式有两种:
1.用setter方式存储数据
2.用构造函数的方式存储数据
从对象中获取数据方法有一种:
getter方式
构造函数/构造器/构造方法 constructor:
构造函数不能有返回类型
构造函数的名称必须跟类名称一样
如果类中没有明确写出构造方法,系统会给出一个默认的构造函数
默认的构造函数 无参数的的空方法,简称空的无参构造
构造函数可以有参数,如果书写了有参构造,那么就建议写一个
空的无参构造
在当前的构造方法中调用本类的其他的构造
请用this(参数列表);
参数列表中的参数可以是0个或多个,但this(参数列表)调用
构造方法的时候,本类必须存在这个构造
成员变量:
类中的属性数据,就是成员变量,作用域整个类
成员变量会给默认初值,具体的值根据数据类型来决定
局部变量:
类中的方法中定义的变量就是局部变量,作用域在声明变量开始
到第一个右大括号结束,使用局部变量必须给初值
如果不使用局部变量就不需要给初值
面向对象的主线:
1.为什么要有面向对象
a.可以用类组织一批不同类型的数据
b.把现实世界的物和事转换到计算中
现实世界的对象--->计算机中的类-->计算机内存中的对象
2.要实现面向对象的手段和途径,关键是类
类:就是一个名字
就是一个模型
就是一个数据结构
就是一种数据类型
用类可以在内存中实例化若干对象
3.有了类,还有内存中用类实例化的对象
类和对象在内存中如何存储
方法区:类定义
堆区:类的对象
栈区:对象的地址
4.保证数据在堆区中存储是安全的且正确的
属性数据是私有的
方法是公有的
用settter和构造给对象存数据
用getter来取出数据
有了上面的知识的铺垫
程序员写的常规类:
就有两种类模型:
第一种:
类中只有私有属性,只有公用的getter and setter
或者有,有参数的构造函数
此种类适合存储数据的
第二种:
类中只有方法,没有属性
此种类适合做功能,而方法所需要操作的数据,
可以通过方法的参数传递进入方法
值传递:(基本数据类型的数据的传递,栈中的数据)
基本数据类型的数据作为方法的参数进行传递
那么在方法中会另外开辟一个新的内存空间,
跟原来的内存空间不是一个内存空间
如果在方法中对参数的值进行更改
不会影响原来的内存空间的值
如果非要获取方法中修改后的值
则需要把修改后的值返回出来
引用传递:(数组的传递,对象的传递,堆中的数据)
如果传递的是对象或数组
实际上是把对象或数组的引用传递到方法中
如果在方法中对引用的对象或数组做任何修改
都会影响原来的内存空间的值,即修改了原来内存
空间,其实操作的是同一份内存空间
this用法:
this有两种形式的用法
-this. 访问当前对象的属性或方法
-this(参数列表) 调用本类的其他构造函数
this指的是当前对象
如何判断this指向的是哪个对象:
this在哪个方法里,这个方法被哪个对象调用
那么this就指向那个对象,这个原则适用于this.
不适用this();
代码块:
代码块就是一对{}括起来的代码段
代码块有两种:
-在类中用代码块括起来的一段代码
类中的代码块在new 构造函数之前执行\
-在方法中用代码块括起来的一段代码
方法中的代码块用于限定某些变量的作用域
类中的代码块的应用场景:
在构造函数之前需要初始化一些数据的时候
final关键字:
final 最后
final关键字可以修饰在成员变量,和局部变量
final关键字可以修饰在方法上
final关键字可以修饰在类上
final修饰在变量上,最后的量值,量值不能修改,所以是常量
比如:
final int i=3;
final修饰的变量,只能赋值一次
final int i;
i=10;
i=100;//报错
final修饰在成员变量上,必须直接给初值
比给初值是语法错,不是运行错
final修饰在局部变量上,可以先声明,后给初值,初值只能赋值一次
final修饰在方法上,那么这个方法就变成最后的方法,是不能重写
final修饰在类上,这个类就是最后的类,final类不能被继承
String类就是final类
静态:
所有静态相关,都是用static关键字修饰
-static关键字可以修饰成员变量,不能修饰局部变量
-static关键字可以修饰方法
-static关键字可以修饰一段代码,静态块/静态代码块 static{代码段}
static修饰的成员变量开辟空间在方法区
所以就是一份内存空间,任何方法都可以修改这个静态
变量,一般静态变量做一些公用或共用的计数,要慎用
,尽量变量做成局部变量,作用域越小,受干扰的可能性
就越小.一般情况下static final 共同修饰成员变量
static修饰在方法,方法也储存在方法区中
非静态方法和静态方法在方法区中存储是没有区别的
都是存储在方法区中,但调用上是有区别的,
非静态方法被调用,必须先实例化此方法所对应的类的对象
然后通过对象打点来调用这个非静态方法
静态方法可以类名直接调用,
如果想频繁的调用一个方法,这个方法不需要操作某个对象
的数据时,这个方法最好设置成静态方法
在静态方法中不能使用this,this属于对象,而不属于类
静态的属于类,而不属于对象
static修饰代码块 static{}
代码块就是包含一些代码逻辑,所以静态块还是存储在方法区
但是静态代码块执行的时机是由区别的;
能写代码逻辑的地方,方法和静态块和代码块中都可以写代码逻辑
方法中的代码逻辑是否被执行,取决于程序员是否调用
静态代码块,在类加载到方法区后,就自动执行静态代码块
代码块,是在静态代码块后,构造函数前执行
执行的顺序,先执行静态代码块,然后执行类代码块,然后执行构造函数块
总结:
静态的方法只能访问静态的变量和静态的方法
即,静态的只能访问静态的
非静态的方法可以访问静态变量和静态的方法
也可以访问非静态的方法和非静态的变量
静态的方法即可以用对象调用(不建议),也可以用类名调用(建议)
a. 第一个类加载,执行性第一个类的静态代码块
b. 第一个类中包含第二个类作为第一个类的属性(注意,new和不new )
new:第二个类加载,执行第二个类的静态代码块
执行第二个类的类代码块
执行第二个类的构造方法
不new:不加载第二个类
c. 执行第一个类的类代码块,注意c步骤和b步骤的顺序
b在c前, 执行顺序是 a,b,c,d
c在b前,执行顺序是 a,c,b,d
d. 执行第一个来了的构造函数
Cannot make a static reference to the non-static field temp
不能用静态的访问/引用非静态的
静态+常量 static final 同时修饰
同时修饰static和final关键字
一般情况下是公有的静态的final的量
比如:
public static final int FILE_BLOCK_SIZE=4*1024*1024;
公有的静态的final修饰的量的名字都是大写,且单词间
用下划线分隔,这是一种约定俗成
static+final的量一般出现在这样的类中
public class SysConstant{
public static final int FILE_BLOCK_SIZE=4*1024*1024;
...
//整个项目中所有用到的固定的常量,且值不需要改变的
//都可以放在此类中
}
使用的时候:
SysConstant.FILE_BLOCK_SIZE来取出值即可
方法的重载:
在同一个类内,有相同的方法的名字,
但参数的个数和类型不同
跟方法的返回值无关
比如:都是求和的功能
//此类是重载的写法
public class calc{
public int sum(int a,int b){
return a+b;
}
public float sum(float a,int b){
return a+b;
}
}
使用:
calc c=new calc();
int result1=c.sum(1,2);
float result2=c.sum(1.2F,3);
//此类不是重载的写法
public class calc{
public int sum1(int a,int b){
return a+b;
}
public float sum2(float a,int b){
return a+b;
}
}
使用:
calc c=new calc();
int result1=c.sum1(1,2);
float reulut2=c.sum2(1.2F,3);
重载的应用场景:
功能相似,但功能有一些细微的差异
封装:之前的内容是封装,
把私有的数据封装,用公有的getter and setter 和构造
存储数据和取出数据,适合存储数据
类中只封装功能,适合做功能的
最终把现实世界的物和事转到计算机中
而类中封装的就是属性和方法
继承:
继承必须以封装为基础,
实现继承就是为了代码的重用,继承充分展现了代码的可复用性
因为现实世界有很多的继承关系,所以把现实世界的物或事
转换到计算机中,就需要java的继承,同时增加类和类的关系
此关系很单一,就是继承关系
所有的非Object类都有父类,如果没有写extends关键字
实际上默认继承了Object
java的根类是Object类
继承是通过extends关键字来实现的,extends后的父亲
只能有一个,所以java是单继承
有了java 的继承,好处就代码重用,
坏处就是类和类之间的关系复杂了
继承中的几个容易混淆的概念:
子类能够继承长辈的非私有的东西(属性和方法)
子类不是拥有长辈的东西,只是子类对长辈的东西有访问性
类里的东西就是本类自己的
有了继承之后,同样要考虑数据的存储问题,对象的数据该如何存取
在子类中利用继承的可访问性调用长辈的方法,用长辈的自己的方法
操作长辈类自己对象中的私有属性
super关键字:
super(参数列表)
参数列表,是可以是0个也可以是多个
调用父类构造,且super(参数列表)只能写在子类构造的第一条语句
super()调用的是父类无参构造,如果不写,系统会自动给添加上
super. 用来调用父类的方法或属性. 前提,父类是否允许子类调用
结论:
做继承是为复用长辈中的的内容(属性和方法),
即,就在子类中使用长辈中的内容(属性和方法)
所以继承优点:可以使用长辈中的内容
缺点:增加了类和类的复杂关系
如果继承的层次越多,子类中
可访问的内容就越来越多
但是,子类并不都需要
在开发中少用继承,多用组合
什么时候用继承:
-继承的层次不要太多
-要使用多态的时候必须是以继承做为前提的
什么时候使用组合:
-想要获取别的类中的方法和属性的时候可以组合
-现用,现组合
整合的方式有两种
方式一:
在类中放置属性/组合属性
public class xx{
//组合另一个Test类的对象
private Test test;
public void method(){
//在此方法中可以使用或操作test对象中的数据
}
}
方式二:
在类的方法中组合数据
public class yy{
public void method(Test test){
//在此方法中使用或操作test对象中的数据
}
}
组合是 has a的关系 has a 有一个
上面的方式一:xx类有一个 test对象
方式二:yy类中的method方法有一个test对象
继承是 is a的关系 is a 是一个
Student is a CommonPerson 学生是一个普通人
Teacher is a CommonPerson 老师是一个普通人
想要获取别的类中的数据
1.用继承
2.用组合
继承中的重写
跨类且继承,子类重写父类的方法
方法重写的若干要求
1.子类重写的方法的权限修饰符必须大于等于父类
2.子类中重写的方法的返回值类型必须小于等于父类
3.如果父类中的方法的返回值是基本数据类型或void
那么子类在重写方法的时候,返回类型必须一致
4.如果父类的返回类型是最终类(final),那么子类
在重写方法的时候,返回类型必须一致
一般情况下,只要父类中要重写的方法的声明跟子类方法的声明
一样即可
重写和重载的区别:
重载:
在类内,相同的方法的名称,但参数的个数和类型不相同
重写:
跨类且继承,子类把长辈的方法体覆盖
访问修饰符:
public:本类中,子类中,同包类中,其他类中
protected:本类中,子类中,同包类中
默认/friendly/default:本类中,同包类中
private:本类中
继承中的一个经典的应用就是equals方法
equals跟==的区别:
==是一个判断相等的运算符
这个运算符的两边如果是基本数据类型,判断的是基本数据类型
的值是否相等
比如:
int i=3;
int j=3
System.out.println(i==j);//true
这个运算符的两边如果是某个类类型的对象,判断的是两个
对象是否是同一个地址,
如果地址相同,那么内容就一定相同
如果地址不相同,内容就不一定相同
equals方法不能判断基本数据类型的数据,只能判断对象是否相等
如果没有重写equals方法,那么equals方法判断的是地址
因为这个equals方法是继承自Object的equals方法,Object
类中的equals方法判断的是地址
如果某个类重写了equals方法,那么就看equals方法中实现的
判断是什么,一般情况下判读单的是equals两端对象的内容.
在java的类库中象String,封装类(Integer,Float,Double...)
中的equals判断的就是内容是否相等.
instanceof 运算符:
instanceof运算符左边是一个对象,instanceof运算符右边是一个类
判断instanceof左边的对象是否实施右边类类型的对象
instanceof运算符的返回值是boolean
比如:
Student stu=new Student();
System.out.println(stu instanceof Student);//true
抽象类:
如果一个类中,有方法没有方法体,那么这个方法就是抽象方法
即,方法只有声明,没有方法体
如果一个类中,只要有抽象方法,那么这个类就是抽象类
说明这个类不具体
抽象类也是类,有类就可以放属性和方法(带方法体,不带方法体)
抽象类中可以放置属性,属性使用来存储数据的
抽象类中可以放置有方法体的方法,此方法能够完成某个功能
抽象类中可以放置没有方法体的方法,这个方法就是抽象方法
只要类中有一个抽象方法,这个类就是抽象类
抽象类是不能实例化对象,因为抽象类有抽象方法
程序员不能实例化,并不等于jvm不能实例化对象
抽象类只能当父类,
如果在子类中把所有的抽象方法都补上方法体,
那么这个子类就不是抽象类了,就可以实例化对象了
同时也可以调用方法了.
如果在子类中没有把所有的抽象方法给补上方法体
那么这个类还是抽象类,满足抽象类的用法.
抽象方法不能用static ,final,private修饰
抽象类的应用场景:
在父类中有一些方法没有方法体,或者没有必要给上方法体
此时把方法做成抽象方法,那么此类就变成抽象类
比如:
Shap形状类中有一个方法叫做draw()绘制图形的方法
Triangle继承自Shap类,draw()方法就可以绘制三角形
Retangle继承自Shap类,draw()方法就可以绘制矩形
Shap类中的draw方法就应该是抽象方法,因为Shap
不代表具体形状.所以没办法复制
Cannot instantiate the type CommonPerson
把抽象类抽象到极致,所有的方法都没有方法体,
此情况不建议做成类,建议做成接口
接口:
接口中的所有的方法都是抽象方法,即没有方法体,接口方法
接口中可以放属性,但属性默认是public static final的
接口不适合存储数据,更适合做功能
而类适合存储数据
接口的特点:
接口中的方法和属性都是公有
接口是不能实例化对象的
必须由子类实现接口,且子类可以实现多个接口,解决java的单继承问题
多个接口用逗号间隔
子类实现接口的关键字implements,
直到子类把接口中的所有的方法都实现了,才可以实例化子类对象
否则子类就是抽象类
接口中可以有属性,但默认的是public static final,不适合存储数据
接口中可以有方法,方法必须都是没有方法体的,且是公有,public可以省略
定义接口的关键字interface
接口不能实现接口,但接口可以继承接口,而且还可以多继承
即接口extends接口1,接口2...
接口中没有构造方法
结论:
接口更适合放置方法,而不适合存数据
类适合存数据,抽象类和普通类
接口就是对子类功能的规范,子类必须全部实现接口中的方法
接口的案例:
Person接口:
study()
eat();
abstract CommonPerson类 实现了Person接口
String name;
int age
abstract study();
abstract eat();
sleep()
Student类 继承自CommonPerson
String stuNo;
重写:是学生的学习方式和吃的方式
study();
eat();
自己的方法
homework();
Teacher类 继承自CommonPerson
int salary;
重写:是老师的学习方式和吃的方式
study();
eat();
自己的方法:
tech()
为什么要使用接口:
1.用接口可以限定子类,必须实现接口规定的所有方法
以保证项目功能的完整性
2.用接口实现多态,有接口就一定有继承,而多态是以继承作为基础的
多态:
顾名思义就是多种表现状态
最直观的理解:有对象名称,用对象名称打点调用方法
在不同的时候执行的方法的功能是不一样的.
*多态的判断标准:
1.跨类且继承,多态是以继承作为基础的
2.子类重写长辈的方法
3.子类对象赋值给长辈的引用
比如:
CommonPerson cp=new Student();
Person p=new Student();
Object obj=new Student();
4.用长辈的引用打点调用方法
-用对象打点能调用出哪些方法,取决于这个对象所对应的类
或接口中的有哪些方法
-如果用长辈的引用打点调用的方法,是重写过的方法,
那么就执行子类重写后的方法.
-如果长辈的引用打点调用个方法是没有重写的方法,
则执行的是本长辈中的方法
能够表达出多态的应用场景:
1.以长辈作为方法或函数的参数
2.以长辈作为方法或函数的返回值
如果不用多态:
-代码的书写量大
-结构不优化
-代码的复用率不高(复用率低)
-最终导致开发效率低
-优点,代码的可读性很高
如果使用多态:
-把所有的共用属性抽象出来放在长辈类中,用于表示数据的吞吐
-把所有共用方法抽象出来放在接口中,用于表示功能
-使用多态会增加类和类之间的关系,导致类和类之间的复杂度增高
-使用多态,可以提高代码的复用率
-使用多态,可以大大提高开发的效率
-使用多态,程序的结构优化
interface IEmployee
double calcSalary();
double riseRate();
abstract class Employee implements IEmployee
String empNo;
int baseSalary;
getter and setter
abstract calcSalary();
double riseRate(){
//计算增长率
}
class CommonEmployee extends Employee
int daySalary;
int days;
getter and setter
double calcSalary(){
//普通员工的计算方式
}
class SaleEmployee extends Employee
int salsMoney;
getter and setter
double calcSalary(){
//销售员工的计算方式
}
class ManagerEmployee extends Employee
int bonus
getter and setter
double calcSalary(){
//经理员工的计算方式
}
class 财务类{//以长辈作为函数的参数
//多态
double calcSalary(IEmployee ie){
ie.calcSalary()
}
//是多态,在抽象类中Employee中重写了riseRate方法
double riseRate(IEmployee ie){
ie.riseRate()
}
}
class 人事{//以长辈作为函数或方法的返回值
//简单工厂
}
抽象类,接口,多态的收尾的小项目
汽车租赁系统
所有的汽车都具备品牌,车牌号,日租金
大巴车Bus:大巴车有很多的座位
普通轿车Car:有排量
商务车Mpv:空间大
根据不同的车型有不同的计算日租金的方法和租车的流程
大巴车:日租金1000,押金10万,3天起租
租车流程是5年内没有任何扣分的A驾照
普通轿车:日租金是400,押金5万,1天起租
租车流程是1年内没有任何扣分的C驾照
商务车:日租金800,押金8万,2天起租
租车流程是3年内,没有扣分的B驾照
要求,应用多态,简单工厂,从键盘录入选择车型
内部类:
顾名思义就是把类放在某个范围的内部
类可以放在包
类可以放在文件中,.java文件
类可以放在类中(类里套类) 内部类
类可以放在方法中(方法类套类) 内部类
1.类中放置内部类
要使用内部类,就需要在外部类中实例化内部类的对象
然后用对象打点调用内部类中的方法
(可以在外部类中,调用内部类的私有方法和私有属性)
通过内部类里的方法来操作内部类的数据,
在内部类中可以直接使用外部类中的所有的方法和属性
要使用内部类,可以在外部类的外面实例化内部类的对象
然后通过对象打点调用内部类的非私有的方法(同一个包内)
2.方法类套类
要使用方法中的内部类,就必须在方法中,内部类定义的下面
实例化内部类的对象,然后通过内部类对象打点,调用内部类
中的属性和方法(包含私有的)
内部类的作用域仅限于当前方法
从内部类定义开始到方法结束,内部类的作用域就消失
在内部类中可以直接使用外部类中的属性和当前方法中的局部变量
在外部类的外部不能使用外部类的方法中的内部类
3.匿名内部类
是内部类,但没有类的名字,但有{属性和方法}
比如:
Person接口
//匿名内部类,但有对象名
Person p=new Person(){
public void study(){
//具体的方法实现
}
public void eat(){
//具体的方法的实现
}
};
p.study();
p.eat();
//匿名内部类,但没有对象名
new Person(){
public void study(){
//具体的方法实现
}
public void eat(){
//具体的方法的实现
}
}.study();
匿名内部类的特点:
当前匿名内部类中实现的方法,只能实现一次
如果还想要其他的实现,则必须用匿名内部类重新new 实现
匿名内部列更适合提供长辈的方法的实现
而且实现只需要实现一次即可
匿名内部类跟有名内部类区别:
匿名内部类的方法只能实现一次,对象也只有一个
有内部类也是实现一次,但可以new很多的内部类对象
jar包:是java中的特殊一种压缩格式的包
jar包中压缩的是class文件
把jar包提供给任何人使用都可以
一般情况下jar包是由某个项目导出成一个jar,
并把jar包提供给需要的项目使用
导出jar包的步骤:
右键单击要到出jar包的项目名称--export--java--jar file
--next--输入jar文件的存储路径和文件名--finish
导入jar的步骤:
右键单击需要jar的项目名称--properties--java build path
--libraries--a.add external jar--选择本地硬盘中的某些jar
b.add library--eclipse中的自带的jar
--ok确定
项目可以依赖项目,也可以依赖jar包
单元测试: junit java unit test java的单元测试
junit是一个大家公认的且广泛使用的测试框架
测试的功能是由junit框架提供的,要使用这个框架
就必须引出junit测试框架的jar包
程序员需要测试程序员自己写方法是否正确,所有需要单元测试
单元测试的版本:
junit3
junit4
junit5
单元测试中的名词:
断言 assert:
断:断定,判定
言:动词,表达的意思
名词,话语或语言的意思
断言就是能够清晰准确表达一个结果是正确的
junit3:
测试类放在test源代码文件夹中(source folder)
测试类必须继承自TestCase类
junit.framework.TestCase
测试类中的方法名称,必须是test为开头方法
比如: public void testMethod(){}
测试类中重写protected void setUp()方法
此方法是在执行任何一个测试方法的时候都要先调用setUp方法
setUp方法执行完毕后,才执行测试方法中的内容
setUp方法适合在执行测试方法前做初始化工作
junit4:
测试类放在test源代码文件夹中(source folder)
在测试方法的前面添加一个注解@Test注解,测试方法命名任意
但建议以test开头的方法
如果在某个测试方法前添加一个注解@Before,那么此方法就在
任何测试方法前执行,一般做初始化工作
异常:
java中有三种错误:
1.语法错误/编译错误:
在ide(集成开发环境)工具(eclipse,myeclipse等)
中写代码,代码写错了,在工具中用红波浪线或红叉
来表示,如果现实红波浪线,此代码是不能通过工具
进行编译的,不能编译,就不能执行
2.逻辑错误:
前提是语法没有错误,程序能编译,能运行,但结果是错误的
一般原因就是代码的逻辑写错了,最终结果也是错的
3.运行错误/异常/例外:
前提是逻辑没有错误,但在运行期间因为某种原因导致错误
,程序意外终止,这种错误就叫做异常
一定要注意,异常是运行期间发生的
比如:
int num1,num2;这个两个值需要从键盘录入
int result=num1/num2;
如果num2录入的是非零值,程序可以正常运行,没有异常
如果num2录入的是零值,程序怒能正常运行,代码逻正确,有异常,且程序终止
有了异常,最直接的结果,就是程序终止了,这不是程序的目的
所以要处理异常,处理异常的目的有两个
1.避免程序意外终止
2.如果出现了异常,程序不能终止,还要给一个友好提示
程序意外终止:就是程序执行到某一行位置的时候,不执行了
jvm就不再继续执行这个程序了
处理的方案有两种
方案一:
用java代码提供的异常处理机制 try catch finally
方案二:
用java的逻辑代码来处理,即用java代码来容错
用java的try catch finally的一些相关的注意事项
一般情况下,try块中监视的代码,需要程序员猜测try块中
有多少种课程产生的异常,在try块中的代码可能有n种异常,
那么就在try块的下面写n个catch,且catch抓取的异常类型是不同的
,如果发生了异常但是,没有catch来抓住,程序还是意外终止的,
为了解决这个问题,需要添加第n+1个catch,这个catch的异常类型是
Exception异常类型,即catch(Exception e),且此catch必须放在
所有的catch的最后
一个try 可以有零个或一个或多个catch
一个try可以没有catch,但必须哟finally
什么是finally:
finally块中放置若干java代码,一般情况下,finally块中的代码
是用来释放try块中种申请的内存空间
try块中如果有异常,会抛出异常,在某一个catch块接收到系统抛出
的异常(必须保证catch抓取的异常类型,跟抛出的异常类型相同)
,然后对应catch块中的代码就被执行了,catch块一般有类代码,
一个是栈的轨迹,另一个是友好的交互或提示的代码
执行完当前catch块中的代码后,会跳过后面所有的catch块,执行
finally块中的代码,执行完finally块后的代码后,继续执行finally
块后面的代码,即没有异常,最终都会执行finally块,用户销毁申请
的内存空间.
异常的继承层次:
顶级类:Throwable
Trowable类有两个子类
Error类:不是程序员能使用的,一般情况下是jvm使用
比如:
要往u盘中写入一个文件,但没有u盘这个设备
异常错要抛给jvm来处理
Exception类:是程序员能够写代码处理的异常的根类
分为两类别:
-免检异常:不是必须添加try catch finally的
程序是能够编译和执行的,执行时
触发的符合的异常程序还是意外终止
RuntimeException 运行时异常
ArithmethicException 算术异常
NullPointerException 空指针异常
ArrayIndexOutofBoundsException 数组下标越界异常
NumberFormatException 数字格式异常
InputMismatchException 输入不匹配异常
...等
-必检异常:必须添加try catch finally,如果不添加,
则会会有红波浪线提示,说明程序时不能编译和执行
Exception 所有程序员能处理的异常的根类
SQlException sql异常
ClassNotFoundException 类没有发现异常
FileNotFoundException 文件没有发现异常
...等
如何判断异常时是免检还是必检
如果程序员在写代码的时候,代码没有错误,但报了红波浪线
并提示"Unhandled exception type XXXException"
无法处理 异常 类型 具体的异常类型
只要提示这句话,那么就属于必检异常
必检异常必须添加try catch.否则红波浪线不会消失
快捷键 alt+shift+z 来调用代码块的模板
throw:
在java中,有一个Exception类,这个类有很多的子类,
每个子类都对应一种异常类型,这些异常类型都是java
类库自带的的异常类型
比如:
NullPointerException
ArrayIndexOutofBoundsException
类似这些异常都java的定义完的规则,触犯规则就抛异常
即,系统定义的规则,系统检测到就由系统抛出
java中不可能把所有项目中遇到的异常都事先写好异常类
由程序员根据实际业务需求来抛出异常
比如:
要求年龄在1到100岁之间,如果不在范围内就抛出相应的异常
程序员定规则,程序员用java代码检测,程序员抛出
实现的方式有两种
方式一:
用try catch finally throw
方式二:
用java的逻辑代码来处理
throws:
throws关键字放在方法声明的后面,可以写很多个异常类型
之间用逗号间隔,表名此方法体可能会抛出的异常,这些异常
不在当前的方法体中用try catch finally处理,谁调用此方法
谁就用try catch finally来处理
注意:Exception类型必须放在throws后所有异常类型的后面
throws 的应用场景:一般情况下,写类库的时候,类库是提供给
其他项目使用的,类中的方法如果有异常,就适合用throws抛出
然后谁调用方法,谁就用try catch来处理,
自定义异常:
程序员自己创建的java类,但是必须继承自Exception及其子类中
的任何类都可以,那么此类就是自定义异常类
自定义异常类,是作为本项目的异常类型补充
应用场景:
java不可能把所有的项目中出现的异常,都事先准备好
所以,程序员根据需求自定义异常类,在自定义的异常类
提供异常错误信息
垃圾收集管理器 GC
是jvm中的一个后台线程,此线程完成的功能是收集堆空间中的
没有引用的对象内存空间,并释放空间
GC的主要特点是能够释放无用的对象空间,程序员是无法掌控GC调用
gc针对的是堆空间
在堆空间中创建的对象是创建在新生代中
新生代区:young generation
新生代分类:
伊甸园区:Eden 所有的新创建的对象都放在此区中
幸存区:Survivor 在eden区中经过一个扫描,此对象还存在
就把对象从eden区移动到此区中
一个eden区对应2个survivor 8:1:1
而最后一个survivor区是空闲的,是系统保留区
老生代区:old generation
在幸存区中多次扫描对象,还存在在幸存区中,就把幸存区
中的对象移动到老生代中
老生代扫描对象是否可用的频率远低于幸存区的扫描频率
如果在老生代中长时间存在的对象,建议程序员把对象放在
方法区中,以减少扫描的频率,程序员在写代码的时候
要考虑对象的作用域空间
minor gc 初代回收,收集的是的新生代的eden区的空间
full gc 完全回收,收集的是老生代的内存空间
java api: api :application interface 应用程序接口
是java开发团队事先做好的类库
类库:就是一些java的类的集合
即,就是一些功能的集合
java api的狭义理解就是java 方法
经常会有这样的说法:
调用api方法/调用api
常用的api
-String StringBuffer StringBuilder
-封装类 Integer Float Double等八种
-Calendar 日历类(待定)
-Object类
-Math类 BigDecimal BigInteger
集合api:
List集合
Set集合
Map集合
IO api:
所有的输入输入流,文件操作
线程 api:
Thread类和Runnable接口
线程池
socket api:
网络编程:tcp/ip编程
Socket类
ServerSocket
集合api:集合框架,把多个对象存储在一起,用集合的名称,
并同时使用集合的api,来操作集合中的数据
有了集合可以让数据的存储变得很复杂
用对象可以存储不同数据类型的数据
把若干对象存储在集合中
集合框架的分类:
List集合/数据结构:线性,数据内容无序,但数据的位置是有序的
位置从零开始的
Set集合/数据结构:非线性,数据的内容无序,位置也无序,但内容不能重复
Map结合/数据结构:键值对 若干件事对组织在一起的数据
使用上面的三种集合
1.集合的初始有一个固定的大小,随着往集合中添加数据,
集合的空间不够,那么集合会自动扩容
2.可以给集合存储若干不同类型对象数据
3.给集合存储完元素后,就可以操作集合的数据
-List:
查找:有位置下标的数据,查找块
没有位置下标的数据,查找的速度不一定
增加:增加到集合末尾,追加,块
增加到集合中的某一个位置,慢
删除:有位置下标,删除快,移动数据慢
修改:
-Set:
查找:查找慢
增加:慢,保证集合中的数据没有重复的
删除:先找后删
修改:
-Map:
查找:有key数据,查找快
没有key数据,查找速度不一定
增加:慢,因为要保证所有的key不能重复
删除:有key删除快
修改:
集合的继承结构:
Collection接口
List接口
ArrayList类 ArrayList implements List
LinkedLies类 LinkedList implements List
...
Set接口
HashSet类 HashSet implements Set
TreeSet类 TreeSet implements Set
...
Map接口
HashMap类 HashMap implements Map
Hashtable类 Hashtable implements Map
List集合
List是一个接口,接口定义了规范,要使用这些接口,
就必须实现这个接口,并给接口的方法补上方法体
ArrayList;类和LinkedList类都是List接口的子实现
ArrayList类:有具体的下标,通过下标查询速度快,不适合做增和删
LinkedList类:不适合做查询,更适合做增和删
List集合常用的api:
boolean add(Object) 给集合添加对象
int size() 获取集合中的元素的个数
Object get(int) 从集合中获取指定下标位置的元素
void clear() 清除集合中的所有的元素
boolean isEmpty() 判断集合是否为空,指的是结合内容为空
List subList(int,int),从起始索引到终止索引,包括头,不包括尾
void remove(int) 删除指定索引位置的对象
void remove(Object),删除指定的对象
void remove(Collection) 删除集合中的一个子集
...等
集合空:
-集合对象为null 空
-集合对象不为null,但集合内容为空 即 size=0
ArrayList特点:
-理解为可变数组
-可以存储null
-下标位置有序,内容无序
-线程异步,速度快,不安全
-实现自List接口
Vector特点:
-用法跟ArrayList一样
-实现自List接口
-可以存储null
-下标有序,内容无序
-线程同步,速度慢,安全
ArrayList可变数组是如何实现动态扩容,以及扩容原则:
申请一个更大的数组空间,
新数组=Arrays.copyOf(源数组,新数组的长度)
ArrayList集合不适合存储大量数据
空间增长规律
jre1.6及以下 old*3/2+1
jre1.7及以上 old+old>>1
默认容量10 16 25 38 100 151
6 9 13 51
泛型:参数化类型.
Set集合:
集合中不能有重复的元素出现,所有的Set集合中的元素,
只能唯一,每有顺序,能存储null,线程不安全
Set接口:
HashSet集合越小,遍历的速度和效率就越好
TreeSet集合是要求集合中的数据排序
要求要存储的元素对象对应的类必须实现Coparable接口
Map集合:
若干键值对数据组成的集合
map集合中的的所有的key(值)也是一个集合,是一个Set集合
说明key不能重复
map集合中的所有的value(值)也是一个集合,是Collection集合
集合中的每一个元素是由key和value组成的,而value可以是
Collection集合或Map集合皆可,即,集合套集合
Map接口的两个子实现:
HashMap:可以存储null,线程不安全,异步
Hashtable类:不可以存储null,线程安全,同步
Map集合的应用场景广泛:
原因:前提是能确定key值,就一定会快速获取key对应的对象
很多框架中得数据都是用map集合存储
Spring框架,springmvc 框架,strutcts框架 ,mybatis框架
tomcat框架,redis框架等...
map的集合只能通过put方式添加集合元素(键值对)
存储完后,会把所有的key构成一个Set集合,此Set集合只能读取
不能添加,同理所有的value组成集合直能读取不能添加
从map集合中取出集合的数据由三种方式:
1.一次取出一个键值对
2.先取出所有的key,循环遍历key,根据key来取出value
3.直接取出所有的value,进行遍历,找到合适的value即可
集合套集合:
解决了更复杂的数据结构问题
ArrayList<List> all=new ArrayList<List>();
ArrayList<Collection> all=new ArrayList<Collection>();
ArrayList<Map<String,List<Collection>>> all=new ArrayList<Map<String,List<Collection>>>()
Map<String,Collection> all=new HashMap<Stirng,Collecition>();
Map<String,Map<String,Collection>> all=new HashMap<String,Map<String,Collection>>();
集合迭代器:是迭代器设计模式的一个很好的实现
在java中只要有迭代器这个词语
迭代 等价理解为 循环 等价理解为 遍历
集合中的迭代,主要用途,就是遍历集合中的元素
把List集合,和Set机会,Map集合都转换成集合的迭代
集合迭代的本质,就是把所有的集合的遍历方式
转换成迭代器这一种遍历方式
实现步骤:
List集合转换成迭代器集合,用迭代器api方法来迭代数据
Set集合转换成迭代器集合,用迭代器api方法来迭代数据
Map集合转换成迭代器集合,用迭代器api方法来迭代数据
Collection和Collections区别:
Collection接口:是List接口和Set接口的父接口,接口中
规范了List和Set集合的操作方式
Collections类:在类中有很多的方法,这些方法都是用来
操作集合的,可以看成是集合的工具类,
且很多的方法是静态方法
集合的排序:
List集合排序:
-Collections.sort(List);
sort方法的参数是一个List集合,对List集合中的数据排序
如果List集合中的元素,每个元素内部只有一个数据,就直接
比较即可,前提要保证元素中的数据类型,必须重写了compareTo方法
如果List集合中的元素,且每个元素中有很多的其他数据,就需要
把元素的类型实现Comparable接口,并重写CompareTo方法
在此方法中指定排序的原则.
-Collection.sort(List,Comparator);
sort方法的参数有两个,一个是要排序的list数据,
另一个参数是比较器接口Comparator接口,在此比较器中
执行要排序的原则,使用比较器的方式就不用对要比较的结合
中的元素类型实现Comparable接口
可以实现多个比较器,每个比较器对应一种排序原则
总结:
如果就一种排序原则,用Comparable接口
如果有多种比较原则,就用Comparator接口
常用的api:
Object类:
是所有类的跟类,此类中放置了一些常用的api方法,这些api方法
是所有的类都需要使用的.
-getClass() 获取指定对象的Class类型的对象
-equals(Object) 判断地址
-hashCode() 返回指定对象的哈希值
-toString() 输出对象的类型+@哈希值
如果两个对象相同,说明是一个对象
只占用一份内存空间,输出的哈希值是一样的
System.out.println(对象名);
等价于
System.out.println(对象名.toString());
-finalize() 在gc收集对象之前会自动调用此方法
在此方法中可以自定义对象的释放原则
是某个类重写此方法,只能在gc收集这个类
的对象的时候才会调用,程序员是无法掌控
何时gc,所以无法掌控此方法的调用
final和finally和finalize的区别
final关键字:
修饰在变量上,就是常量
修饰在方法上,不能被重写
修饰在类上,不能被继承
finally关键字
在异常处理中用于释放资源的
finalize方法
在gc收集对象前调用此方法
String类
-java.lang.String使用的final修饰的类,不能被继承
-字符串底层封装的是字符数组,以及操作字符数组的的api方法
-字符串一旦创建,对象将无法更改,但字符串的引用可以重新复制
-Java字符串采用的unicode编码
String常量池:
-java为了提高性能,静态的字符串(字面量,常量)在常量池中创建
并尽量使用同一个对象,重用静态字符串
-对于重复出现的字符串,jvm会首先在常量池中查找
如果存在就返回该对象
比如:
String str1="abc";//abc字符串放置到常量池中,在栈中引用是str1
在另一个作用域中
String str2="abc";abc在常量池中寻找是否有相同字符串,如果有就指向他,没有在常量池中添加
String的常用的api方法:
indexOf方法:用户在字符串中检索另一个字符串(从前往后找)
-int indexOf(String str)
在字符串中检索str,返回第一次出现的位置,
如果没有找到,就返回-1
-int indexOf(String str,int fromIndex)
从fromIndex位置开始检索str
lastIndexOf方法:用户在字符串中检索另一个字符串(从后往前找)
-int lastIndexOf(String str)
在字符串中检索str,返回第一次出现的位置,
如果没有找到,就返回-1
-int lastIndexOf(String str,int fromIndex)
从fromIndex位置开始检索str
indexOf方法和lastIndexOf方法返回结果都是整数,找到字符串的位置
没有找到返回-1
subString方法用户返回一个字符串的子字符串
-String subString(int beginIndex,int endIndex)
返回一个子字符串(包头不包尾)
-String subString(int beginIndex);
从beginIndex开始一直到末尾
trim方法用于去掉字符串两边的空格
-String trim();
charAt方法,返回指定位置的字符
-char charAt(int index);
如果超出范围会报数组下标越界异常
StringIndexOutBoundException
startsWith方法 检测一个字符串是否以指定的字符串开头
-boolean startsWith(String str)
endsWith方法 检车一个字符串是否以指定的字符串为尾部
-boolean endsWith(String str)
toUpperCase方法,转换字符串中的所有字符为大写
toLowerCase方法,转换字符串中的所有字符为小写
valueOf方法 将其他类型转换为字符串类型(静态方法)
split方法 根据指定的规则,拆分字符串
-String[] sprit(String str)
equals方法,判断内容,因为重写了Object的equals方法
equalsIgnooreCase方法,忽略大小写比较内容
StringBuilder类
封装的是可变字符串,对象创建后可以通过调用本类的api方法
改变其封装的字符串序列
-StringBuilder append(String str);//追加字符串
-StringBuilder insert(int destOffset,String str);//插入字符串
-StringBuilder delete(int start,int end);//删除指定的开始和结束中间的字符串(包头不包尾)
-StringBuilder replace(int start,int end ,String str);
-StringBuilder reverse();字符串反转
方法链:
方法的返回值都是本类对象就可以用方法链
比如:
StringBuilder sb=new StringBuilder("abc");
sb.append("de").insert(2,"哈哈哈");
结果 ab哈哈哈cde
StringBuffer类:
StringBuffer类跟StringBuilder类用法基本相同
String stringBuilder StringBuffer区别
-String字符串序列不可变, 里边的字符数组是final
-StringBuilder和StringBuffer都是字符串序列可以改变
-StringBuilder线程不安全,异步,速度快
-StringBuffer线程安全,同步,速度慢
-如果频繁对字符串的内容做增删改
首选StringBuilder或StringBuffer
-如果字符串内容不改变,首选用String
正则表达式:
正则表达式就是记录文本规则的代码
比如:
[a-z] 表示a到z的任意一个字符
[a-z]+ 表示1个或多个a-z的字符组成的字符串
字符集合:
[abc] a,b,c中任意一个
[^abc] 除了a,b,c的任意一个字符
[a-z] a-z中任意一个字符
[a-zA-Z0-9] a-z,A-Z,0-9中任意一个字符
[a-z&&[^bc]] a-z中除了b,c以外的任意一个字符
预定义字符集
\. 任意一个字符
\d 任意一个数字字符 相当于[0-9]
\w 单词字符 相当于[a-zA-Z0-9_]
\s 空白字符[\t\n\r\f]
\D 非数字字符
\W 非单词字符
\S 非空白字符
数量词:
X? 表示0个或一个X
X* 表示0个或任意多个X
X+ 表示1个到任意多个X (大于等于1个X)
X{n} 表示n个X n代表一个数字
X{n,} 表示n个X到任意多个X (大于等于n个X)
X{n,m} 表示n个到m个X
比如:定义6位数字
[0-9][0-9][0-9][0-9][0-9][0-9]
\d\d\d\d\d\d
\d{6}
分组 ():
表示分组,可以将一系列正则表达式看做是一个整体
分组是可以使用"|"表示或的关系
比如:
(\+86|0086)?\s?\d{11} +86 13800138000 +0086 13800138000
(\+86|0086)?\s*\d{11} +86 13800138000
便捷匹配:
^ 代表字符串的开始
$ 代表字符串的结束
封装类:
基本数据类型 封装数据类型
byte Byte
char Character
short Short
int Integer
long Long
float Float
double Double
boolean Boolean
void类型 Void
为什么要使用封装类:
8种基本数据类型,给指定的基本数据类型存储完数据
基本数据类型不能通过方法来操作数据,不是面向对象的操作方式
封装类解决了这个问题,先把数据存储给封装类的对象
然后通过封装类的对象打点调用方法,通过方法可以对
对象向中的数据做各种操作
比如:
用面向对象的思维实现
Integer intObj=new Integer(10);
把整型数据转换成字节
byte byteValue=intObj.byteValue();
不用面向对象的思维实现
int i=10;
byte byteValue=(byte)i;
拆箱和装箱:
拆箱:把类类型拆开变成基本数据类型,即把对象中的数据拆出来
变成基本数据类型的数据
装箱:是把基本数据类型的数据封装成对象数据
比如:
方式一:
Integer i=new Integer(3);//把基本数据类型数据3装箱成对象i
int ii=i.intValue();//把i对象中的数据3,转换成基本数据类型ii
方式二:
Integer k=3;//装箱
int kk=k;//拆箱
方式一的写法在任意jre版本都能使用
方式二的写法只能在jre1.5及以上的版本,
此方式是属于自动装箱和拆箱
数学相关的类:
-Math类:
1.Math类是final类
2.构造方法私有,不可以创建对象
3.主要的用法是Math类提供了大量的静态的方法
4.在计算小数的时候不够精确
-BigDecimal类:
1.用于精确计算的类
2.在精确急死俺的时候要求参数以字符串的方式传入此类的对象
-BigInteger类:
1.用于存储任意大小的整数的类
2.在存储数据的时候最好用字符串的方式传入对象
日期相关类:
-Date类
1.表示日期的类
2.提供很多的操作日期的方法,但很多的方法被java标记为过时(Deprecated)
-SimpleDateFormat类
1.parse方法,将日期的字符串转换成日期
2.format方法,将日期对象转换为日期的字符串
3.在转换的时候可以提供转换的格式
-Calendar类
1.有关日历相关的类,控制时区
2.提供大量的方法,来操作时间
3.Calendar类是一个抽象类,不能直接new 实例化对象
Calendar cal=Calendar.getInstance();
总结:
用Date对象存储日期的数据
用Calendar类的对象,操作Date对象中的日期数据
用SimpleDateFormat做Date对象和日期字符串相互转换
java IO api
input输入 output 输出
所有的往内存中送数据的都是输入
所有的从内存中出数据的都是输出
能用java.io包的api方法操作的输入输出
内存-->外存(硬盘,光盘,U盘) 本地流输出
内存<--外存 本地流输入
结合Socket网络编程
内存-->网络上 网络流输出
内存<--网络上 网络流输入
网络流的案例
计算机A 和远程计算机B
从计算机B的计算机上下载文件到计算机A上
1.先从B中的硬盘上本地流输入,把硬盘上文件读入到B的内存
2.把内存中的文件数据网络流输出,把内存数据输出到网络上
3.计算机A网络流输入,把网络上的文件数据读入到A的内存
4.把A内存中的数据,本地流输出到A的硬盘上
上传文件的过程跟下载是相反的
不能用java.io包操作的流
内存-->显示器
内存-->cpu
内存<--cpu
数据的持久化:
-数据长时间保留在硬盘上
-数据上时间保存在数据库上,其实数据库本质,是以数据文件的方式持久化到硬盘上
在硬盘中的实际体现出来的是文件和目录
java中提供了一个java.io.File类用来操作文件的目录信息和文件的信息
就是不能操作文件的内容
根据文件的内容操作分为
字节流:对文件内容读写用字节的方式操作
字符流:对文件内容读写用字符(ascii)的方式操作
但是其本质底层还是用的字节流
java.io.File类:用户标识文件和目录的,跟文件内容无关
注意一个问题:在不同的操作系统上对于目录的间隔符的区分
windows:
c:\aa\bb\cc.txt
linux:
/home/aa/bb/cc.txt
在java中对路径的分隔符的表示
window方式:
c:\\aa\\bb\\cc.txt
c:/aa/bb/cc.txt
linux:
c:/aa/bb/cc.txt
如果想兼容window和linux
"aa"+File.separator+"bb"+File.separator+"cc.txt"
File的api:
-构建File类对象
File(String filePath);
File(File parent,String child);
File(String parentName,String child);
-isFile()
判断是否是文件
-isDirectory()
判断是否是目录
-length()
获取文件的长度
-exists()
判断文件或目录是否存在
-createNewFile()
创建一个空文件
返回值是boolean
如果指定的文件不存在,就创建文件并返回true
如果指定的文件存在,则返回false;
-delete()
删除文件
注意:
如果File表示一个目录
删除的时候,要保证目录必须空的
-mkdir()
创建目录
-mkdirs
创建多个目录
-listFiles()
返回指定目录中的所有的文件和目录
-listFiles(FileFilter)
返回指定目录中的部分文件和目录,用FileFilet设定筛选条件
-listFiles(FilenameFileFilter)
返回指定目录中的部分文件和目录,FilenameFileFilter设定筛选条件
总结:
1.只能操作文件或目录的信息
2.就是不能操作文件的内容
RandomAccessFile类,
-可以操作文件的内容,
-按照字节操作,字节流
-read读和write写都是此类中的api方法
-能够通过seek方法可以随意移动/改动文件的指针
RandomAccessFile类对文件的随机访问有两种模式
只读模式
读写模式
-创建对象
RandomAccessFile(File file,String mode);
创建从中读取和向其中写入的随机访问流
文件通过File指定,
模式通过String指定
RandomAccessFile(String name,String mode);
创建从中读取和向其中写入的随机访问流
文件通过String指定,
模式通过String指定
mode的取值:
"r" 只读模式 read
"rw" 读写模式 read write
-写入操作
void write(int d);
此方法会根据当前指针所在的位置处写入一个字节,
只能使用整型的低8位
void write(byte[] d)
此方法会根据当前指针所在的位置处写入一组字节,
void write(byte[] d,int offset,int len)
将len个字节从指定的byte数组写入文件,并从偏移量offset处开始
-读取操作
int read()
从文件中读取出一个byte字节,填充到整型的低8位
如果返回-1,表示读取到文件的末尾 EOF end of file
int read(byte[] b)
从指针指向的位置开始读取若干字节,存储到字节数组中
将读取到的字节按照顺序放在字节数组的相对应的位置
返回值为读取到字节数,也可以说成读取到的长度
返回值为-1,则读取到文件的末尾
int read(byte[] d,int offset,int len)
将最多len个数据字节从文件中读入byte数组中,
并从偏移量offset开始存储
-void getFilePointer()
返回此文件中的当前的偏移量
-void seek(long position)
设置到此文件开头0到文件指针偏移量,在该位置发生下一个读取
或写入操作
-int skipBytes(int n)
用此方法可以跳过一些少量的字节,只能是跳正数
字节流:
字节流:可以从或向一个特定的方向读写数据,数据是字节
封装流/处理流:针对字节流进行封装,即对一个已经存在的流
进行封装,通过所有的封装流能够对数据更有效
的读写,封装流的底层还是字节流
通常字节流被称之为低级流
处理流被称之为高级流或过滤流
InputStream 是所有的字节流的父类,其定义了基础的读取方法
- int read()
读取一个字节,以int的形式返回,该int的低8位有效
否则返回-1,表示文件末尾EOF end of file
- int read(byte[] b)
尝试最多读取给定数组length个字节,并存入该数组
返回值为实际读取的字节量的长度,否则返回-1,到文件末尾
- int read(byte[] b ,int offset,int len)
将输入流中的最多len个数据字节写入byte数组,
将从offset的位置开始写入数组,len不能超过数组的实际长度
如果超过会报数组下标越界异常
- void close()
关闭此输入流并是释放与该留关联的所有的系统资源
OutputStream 是所有的字节流的父类,其定义了基础的写出方法
- void write(int d)写出整型数据的低8位
- void write(byte[] b)将给定的字节数组的数据全部写出
- void write(byte[] b,int offset,int len)
将给定的字节数组从偏移量offset开始的len个字节写入输出流
- void flush()
刷新此输出流并强制写出多有缓冲的输出字节
- void close()
关闭此输出流并是释放与此输出流有关的所有的系统资源
FileInputStream:文件输入流
-可以操作文件内容
-操作的是字节流
-继承自InputStream抽象类
-低级流
-操作的是文件
FileOutputStream:文件输出流
-可以操作文件内容
-操作的是字节流
-继承自OutputStream抽象类
-低级流
-操作的是文件
缓冲流:Buffer缓冲 高级流之一
缓冲流的原理:
向硬件存储设备操作数据,导致增大跟硬件的交互次数,
会降低读写的速度,做缓冲流的目的就是为了尽量减少
跟硬件的交互次数
缓冲输出流原理:BufferedOutputStream缓冲输出流内部会维护
一个缓冲区,每当我们向该流写出数据时,都会先将数据存入缓冲区
当缓冲区已满的时候,缓冲流会将数据一次性写出
注意:
void flush(),清除缓冲区,将缓冲区中的数据强制写出
以保证数据完整
缓冲输入流原理:BufferedInputStream缓冲输入流内部维护一个
缓冲区,每当我向该流读入数据,都会先将数据存入缓冲区,
BufferedInputStream的read方法会从缓冲区去读取数据,
当缓冲区全都读取完毕,如果再次read的时候,会在一次
把缓冲区填满,read在逐一从从缓冲区中读取数据,以此往复
缓冲是靠牺牲内存来提升io的读写效率
对象流:高级流之一
把内存的对象数据,序列化到硬盘上,也可以把硬盘上的文件
反序列化会内存的对象
序列化:
把内存的对象序列化到硬盘上,以字节的方式体现
反序列化:
把硬盘上字节序列,反序列化回内存中的对象
比如:
Studnet stu=new Student("张三",20,"S001");
stu-->硬盘上(序列化)-->内存堆中出现stu对象(反序列化)
注意:
如果要实现对象的序列化和反序列化,就必须对序列化的对象
所对饮的类实现java.io.Serializable接口
且类中最好给提供一个long类型的序列化的版本号
比如:
public class Student implements Serializable{
private static final long serialVersionUID = 1L;
fileld 属性
method 方法
}
java.io.Serializable接口:
此接口仅表示可序列化语义,某个类实现这个接口,
就是说这个类表示了可以序列化这个语义,这个类的子类
也同样具备序列化语义,这个类需要提供一个常量serializableUID
用来表示本类的序列化版本号,
如果想跨平台,就需要显示声明一个版本号,且平台两端的版本
必须相同
序列化类中可以有很多的属性,但部分属性不想被序列化和
反序列化,把类中的不需要序列化的属性前加上transient修饰符
transient:瞬间的,短暂,临时的
public class Student inplemntes Serializable{
private static final long serialVersionUID = 1L;
private transient String name;//不可以序列化
private int age;//能序列化的
field 属性
method 方法
}
数据流: 高级流之一
DataInputStream 数据输入流,适合对java基本数据类型的输入
构造函数
DataInputStream(InputStream);
api方法:
readInt();
类似的方法 readXXX();XXX代表的是具体某种类型
DataOutputStream 数据输出流,适合对java基本数据类型的输出
构造函数:
DataOutputStream(OutputSteam)
api:
writeInt();
类似的方法 writeXXX方法,XXX代表的是具体某种类型
字符流:高级流
针对字符流做低级流的二次或三次的封装或处理,字符流也是高级流
字符流的本质还是字节流
Reader类:
-是所有字符流的父类
-是一个抽象类
- int read()
读取一个字符,是占用整型数据的低16位,低16位有效
- int read(char[] chars)
读取一个字符数组的length个字符,并存储到字符数组中
返回的是实际读取的字符量
- int read(char[] chars,int offset,int len)
读取len个字符,存储给字符数组中,以offset位置为起点
Writer类:
-是所有的字符流的父类
-是一个抽象类
-void write(int c)
写出一个字符
-void write(char[] chars)
写出一个数组的子符数据
-void write(char[] chars,int offset ,int len)
写出数据,从offset开始,取len个字符
-void write(String str);
写出一个字符串
-void write(String str,int offset,int len)
写出字符串数据中的部分数据
InputStreamReader类:字符输入流
-可以设置字符集
-按照指定的字符集输入数据
-将字节按照指定的字符集读入字符串数据
-继承自Reader类
OutputStreamWriter类:字符输出流
-可以设置字符集
-按照指定的字符集输出数据
-将字节按照指定的字符集写出字符串数据
-继承自writer类
缓冲字符流:缓冲字符流自己维护一个缓冲的字符数组
BufferedReader类:缓冲字符流输入
-String readLine();//读一行字符数据,读到末尾为null
BufferedWriter类:缓冲字符流输出
PrintWriter类:
-特殊的类,只有输出,没有输入
-是具有自动行刷新的缓冲字符输出流
线程:
并发:
-多个不同的软件同时运行
windows/linux等操作系统,同时管理多个软件并发执行
eclipse,notepad++,chrome,各类游戏等软件同时运行
-一个软件可以被多个用户同时请求
多个浏览器用户同时请求淘宝,做结算操作,支付操作
等价于,结算操作和支付操作,在服务端被多次运行
总结:
多个软件同时运行
一个软件被运行多次
就是要cpu不停的工作,提高cpu的利用率
计算机是如何做到并发:
前提:一台计算机,一块cpu,一个核
分析硬件cpu:
在cpu的某个时间点,只能有一个程序在执行
为了能做到并发,把cpu的时间分成若干时间片段
(时间片段的划分原则由操作系统来定义)
,每个小的时间片段只能执行一个程序
时间片到,就把程序强制离开cpu,再从内存中
选出下一个程序继续运行,以此往复,
看似在一段时间内,是多个程序在同时运行
分析软件:
一个软件动辄几百兆或上G,一个软件是不能整个存储到内存中的
进程:把大的软件分割成多个小的程序段,此程序段可以称之为进程
多个大的程序要运行,先把程序分割成多个程序段,
然后把每个程序的前一部分的若干程序段加载到内存
最终内存中放置了多个程序的多个程序段,这些程序段
要根据优先级排队,cpu从队头获取下一个程序段来运行
大量的程序段/进程,在频繁切换cpu的时候会占用cpu的时间
做压栈和弹栈的工作,和需要部分内存存储栈中的数据
总结:
要想做到并发
1.cpu要划时间片段
2.程序要划出进程/程序段
3.由操作系统来划时间片和程序段
线程:thread,因为并发导致占用cpu的时间和内存,那么就把进程再次
划分为多个更小的程序片段叫做线程,其实在cpu上来回切换
的是线程,因为所有的线程共享进程的资源
在进程中的包含的一个或多个线程是能执行的独立单元.
结论:
cpu划片段
程序划分进程段
进程段划分线程段
真正在cpu运行的是线程
尽量提高cpu的利用率,减少使用内存
进程还拥有一个私有的虚拟的内存地址,该空间仅能被他包含的线程
访问,线程只能归属某一个进程,并且他只能访问该进程的所有
的资源
当操作系统创建了一个进程后,该进程就会自动申请一个名为
主线程或者首要线程的线程
同类的多个线程共享一个块内存空间和一组资源
线程本身有一个供程序执行的堆栈,线程在cpu切换时
负荷小,因此线程被称之为轻负荷进程,一个进程可以
包含多个线程.
进程和线程的区别:
一个进程至少有一个线程
线程的划分尺度小于进程,使得多个线程并发性高
另外,进程在指定的过程中拥有独立的内存单元,而
多个线程共享进程的内存空间,从而提高了程序的运行效率
线程在执行的过程中与进程区别在于每个独立的线程有一个
程序的运行入口,顺序执行序列和程序出口
但线程不能独立运行,必须依存在应用程序中,
由应用程序提供对线程的控制
线程的应用场景:
线程通常用于一个应用程序中需要同时完成多个任务的情况
我们可以将每个任务定义一个线程,使得他们得以一同工作
也可以用于单一线程中完成,但是使用多线程可以更快完成
所需要的功能
线程的使用:在java中能通过java的api做线程编程
方案一:继承Thread类
Thread类:
此类是线程类,其每一个实例对象表示一个可以并发
运行的线程,我们可以通过继承此类并重写run方法
来定义一个具体的线程,在run方法中体现的是线程的
功能,(有线程的入口和线程出口),启动线程时,调用
线程对象的start方法,而不是直接调用run方法
start方法将线程纳入线程调度,使当前线程可以并发运行
当线程获取到cpu时间片后,开始自动运行run方法中的逻辑
代码.
方案二:实现Runnable接口
Runnable接口
实现此接口并重写run方法来定义线程类,然后再创建线程
的时候,将Runnable接口的实例传入并启动线程
这样做的好处可以将线程和线程要执行任务逻辑分离,
减少耦合,同时java是单继承,定义一个实现Runnable接口
这样做,可以更好的去实现其他的接口和继承父类
做线程的目的,实际上是为了做多线程,多线程的目的就是为了能并发
能并发的目的就是为提供cpu的利用率
做多线程的两种方式:
1.多个线程共享一个run
2.多个线程可以每个线程一个run
1和2的组合也是多线程
java程序员可以写线程,不需要程序员关心线程在哪个进程里
继承Thread类
public class MyThread extends Thread{
public void run(){
//线程的入口
//线程的业务/任务逻辑
//线程的出口
}
}
使用:
MyThread mt=new MyThread();
mt.start();//调用重写的run方法,实际上是多态
实现Runnable接口:
public class RunLuoJi implements Runnable{
public void run(){
//线程的入口
//线程的业务/任务逻辑
//线程的出口
}
}
使用:
Thread t=new Thread(new RunLuoJi());
t.start()//执行的是Thread中的run方法,由Thread的run方法中调用接口的run方法
线程的状态:
创建线程对象 创建态 Thread t=new Thread();
创建完的线程对象 就绪态 t.start();//排到线程的队列中,不一定马上获取cpu
获取到cpu资源, 执行态 执行run()方法;//线程出队列
执行到某一个点时有 阻塞态 从cpu上下来;//io,sleep,wait等阻塞
如果正常执行完run方法结束态/消亡态 等待GC回收
线程状态转化:
创建态-->就绪态
就绪态-->执行态
执行态-->阻塞态/挂起
执行态-->就绪态
阻塞态-->就绪态
执行态-->结束态/消亡态
结论:3
1.根据业务,要考虑有多少个业务逻辑,就写多少个run
2.每一个run要执行多少次
3.执行每一次run的时候,run是否使用数据,要考虑数据的安全性
用内部类创建线程对象
通常我们可以通过匿名内部类的方式创建线程,使用该方式可以
简化编写代码的复杂度,当一个线程仅需要一个实例时,我们通常
使用如下方式
方式一:实现类是匿名的,对象有名
Thread t=new Thread(){
public void run(){
System.out.println("run方法");
}
};
t.start();
方式二:实现类是匿名的,对象也是匿名的
new Thread(){
public void run(){
System.out.println("run方法");
}
}.start();
方式三:实现类是匿名的,对象不是匿名的,有对象名
Runnable r=new Runnable(){
public void run(){
System.out.println("run方法");
}
};
Thread t=new Thread(r);
t.start();
方式四:对象是匿名,实现类也是匿名的
new Thread(new Runnable(){
public void run(){
System.out.println("run方法");
}
}).start();
方式五:实现类是匿名的,对象是有名的,对象名t
Thread t=new Thread(new Runnable(){
public void run(){
System.out.println("run方法");
}
});
t.start();
创建线程对象:
1.继承自Thread类,重写run方法
优点:在当前的线程类中可以获取run方法并重写
同时在当前线程类中也可以访问Thread类中的方法
缺点:当前线程类不能继承其他的类,java是单继承
2.实现Runnable接口,并重写run方法
优点:当前类可以多实现,还可以继承一次
把线程对象和线程的任务逻辑代码分离出来
缺点:Runnable接口的实现类只有一个run方法
要想使用其他的线程方法需要
Thread t=new Thread(Runnable接口的实现类对象);
3.特殊的用法,匿名内部类
优点:写法简单,编码量少
缺点:run的实现只有一次
线程的api:
-static Thread currentThread();
获取当前的线程对象
-long getId();
获取线程的标识符
-String getName();
获取线程的名字
-int getPriority();
获取线程的优先级
优先级有10级 1----10(高)
-boolean isAlive();
获取当前线程是否为活动状态
-boolean isDaemon();
获取当前线程是否为守护线程
-boolean isInterrupted()
获取线程是否中断
-static void sleep(long 毫秒数);
指定的毫秒数内让当前正在执行的线程休眠(暂停执行),
此操作受操作系统计时器和调度程序精度和准确度的影响
守护线程:当进程中只剩下守护线程时,所有的守护线程强制终止
联合线程:
void join()
此方法用于等待当前线程结束
比如:
t1.join();//t1线程没有结束,当前线程不会结束
线程的重要的几个关键点:
1.线程何时启动,即线程对象.start();是有先后顺序的
有可能某个线程的启动要靠他的主线程是否启动
2.run方法的执行顺序(run并发执行),靠的是何时start()后
何时能获取到cpu
3.可能run并发执行着,需要注意线程之间的依存关系
当前线程是否执行完毕,是依赖另一个线程是否执行完毕
run都执行,但结束是有顺序的(联合线程,join方法)
synchronized关键字,同步,反义词 asynchronized 异步
同步资源,同步锁
-此关键字可以修饰在方法上
比如:
public synchronized void method(){
//代码块
}
当线程1调用method方法时,就会给method方法添加一个锁
如果方法不执行完毕,其他线程就不会执行此方法,线程排队等待
-此关键字修饰对象上
比如:
synchronized(某个对象){
//同步代码块
}
当线程1调用同步代码块时,就会给对象添加一个锁
如果代码块不执行完毕,其他线程就不会执行此代码块,线程排队等待
-此关键字修饰在类上
比如
synchronized(类名.class){
//同步代码块
}
当线程1调用同步代码块时,就会给类添加一个锁
如果代码块不执行完毕,其他线程就不会执行此代码块,线程排队等待
用户写线程有两种方式
方式一:
用户自己写线程的并发
Runnable r=new Runnable(){重写run方法}
Thread t1=new Thread(r);
Thread t2=new Thread(r);
...
用户要自己控制线程对象的作用域,
相当于用户自己来管理线程
方式二:
java给提供一个线程池的概念
线程池:把若干用户线程添加到线程池中,由线程池来管理线程
为什么要使用线程池:
1.减少了创建和销毁线程的次数,每个工作线程都可以被重复使用
或利用,可以并发执行多个任务
2.可以根据系统的承受能力,调整线程池中的工作线程的数目
防止因为消耗过多的内存,而使服务器宕机(down)
结论:就是java给提供一个写api方法,用于更好的管理线程,
让程序员主要用于写线程逻辑
线程池的使用:
有一个Executors的线程工具类,此类提供了若干静态方法,
这些静态方法用于生成线程池的对象
1.Executors.newSingleThreadExecutor();
创建一个单线程的线程池,这个线程池只有一个线程在工作
即,单线程执行任务,如果这个唯一的线程因为异常结束,
那么会有一个新的线程来替代他,因此线程池保证所有的任务
是按照任务的提交的顺序来执行
2.Executors.newFilxedThreadPool()
创建固定大小的线程池,每次提交一个任务就创建一个线程
直到线程达到线程池的最大大小,线程池的大小一旦达到最大
就会保持不变,如果某个线程因为执行异常而结束,那么就会
补充一个新的线程
3.Executors.newCacheedThreadPool();
创建一个可以缓冲的线程池,如果线程大小超过了处理的任务
所需要的线程,那么就回收部分线程,当任务数增加的时候,此
线程池又可以智能的添加新的线程来处理任务
此线程池不会对线程池大小做限制,线程池的大小完全依赖操作
系统能够创建的最大的大小
4.Executors.newScheduledThreadPool();
创建一个大小无限制的线程池,此线程池支持定时以及周期性
的执行任务的需求
网络编程:
分类:
tcp编程(重点):tcp传输控制协议,能够保证在数据传递不丢失
udp编程(略掉):udp,不保证数据能够传递到对象,数据可能丢失
若干名词:
-socket套接字
-ip地址
ipv4 4段地址
ipv6 6段地址
用于唯一确定网络上的计算机
-端口号:
每台计算机都有65536个端口 0--65535
是能够访问计算机的"门"
特殊的内置端口:
80端口: http协议的默认端口 超文本传输协议
21端口: ftp协议的默认端口 文件传输协议
25端口: smtp协议默认端口 邮件传输协议
-tcp/ip:传输控制协议/网络协议
socket套接字:
用于描述ip地址和端口号,是一个通讯链的句柄(java的引用)
在internet上的一个主机,一般运行多少个服务的软件,同时
就提供多少个服务,每个服务监听一个端口,不同的端口
对应不同的服务
最终应用程序和服务器通过socket套接字建立网络连接
发送请求和接受请求
服务端套接字:
ServerSocket类:
Socket accetp();
用于监听并接受到套接字的连接
void close()
关闭socket
InetAddress getInetAddress()
返回此服务器套接字的本地地址
int getLocalport()
返回此套接字在其上的监听的端口号
客户端套接字
Socket类:
Socket(String host,int port);
创建一个套接字并将其连接到指定的ip地址,和这个ip地址
对应的端口号
void close()
关闭socket
InetAddress getInetAddress()
返回此服务器套接字的本地地址
int getLocalport()
返回此套接字在其上的监听的端口号
jdk1.5新特性:
自动拆箱和装箱(之前有,略)
增强for循环(之前有,略)
可变参数:
-用...来定义
-本质是一个数组
-可以传入任意个参数
-可变参数只能放在方法参数的最后一个位置
静态导入:
-用import static 包名.类名.静态属性名或静态方法名
-可以提高加载效率
-降低了可读性,建议要慎用
枚举:enum
当取值为几个(有限)固定的值,可以使用枚举类型
枚举是一个数据类型
普通的枚举:
public enum RequestMethod{
GET,POST,DELETE,PUT
}
public enum Week{
Monday,
Tuesday,
wednesday,
Thursday,
Friday,
Saturday,
Sunday
}
枚举也可以有方法和属性和构造方法,
但是构造方法必须是私有的.
枚举还可以实现接口,不能进行继承,枚举也可以包含抽向方法
所有自定的枚举都默认继承自java.lang.Enum类
String name();
int ordinal();
什么是反射:
java中提供的一套类库,通过这套类库
-运行时动态获取类中的信息
-运行时动态的调用构造函数创建对象
-运行时动态访问(调用)对象的方法和属性
这种运行期间动态获取类中的信息(属性,方法,包,注解等)
以及动态调用对象的方法和属性的功能称之为java语言的反射机制
通俗的理解,在运行期间对类的内容进行操作
Class类:
要想使用反射,就要获取到类中的所有的信息(属性,方法等...)
在java中有一个特殊的类类型是Class,此类型的对象中存储的是
某个类中的信息
比如:
Class clazz1=Class.forName("cn.tedu.Student");
clazz1是一个对象,对象中存储的都是数据,这些数据
都是Student中的属性和方法
属性:访问修饰符 类型 属性名
方法:访问修饰符 返回类型 方法名(参数列表)
Class clazz2=Class.forName("cn.tedu.User");
clazz2是一个的对象,对象中种存储的是User类中的属性和方法
Class类是java提供的,这个Class类可以表达任意一个类的
的信息(属性方法等类中的信息)
-每个类加载后(方法区),系统都会为该类生成一个对应的Class
类类型的对象,这个对象是存储在堆区中,通过该Class对象
可以访问方法区中的类的信息
-一旦获取了某个类Class类型的对象之后,程序员可以写程序
调用Class对象中的api方法,获取给Class对象中的类的信息
-所有的基本的数据类型也有Class对象
Class clazz=int.class;
如何获取Class类型的对象
-对象.getClass();
比如:
User user =new User();
Class clazz1=user.getClass();
-类名.class
比如:
有一个类是User
Class clazz=User.class;
-Class.forName("包名.类名");
比如:
Class clazz=Class.forName("cn.tedu.User");
此种情况分为两步
1.加载类User,进方法区,创建Class类型的对象
2.返回Class类型的对象
用以上的三种方式可以获取到Class类型的对象
通过Class类型的对象获取如下:
-Field类:代表的成员变量,即属性
-Mehtod类:代表的是方法
-Constructor类:代表的是构造函数
-Annotation类:代表的是注解
通过上面的类的api获取对一个的信息
-可以获取Field中的信息,获取类的属性,属性的修饰符 属性的类型 属性名称
-可以获取Method中的信息,获取类的方法,修饰符 返回类型 方法名(参数列表)
-可以获取Constroctor中的信息,获取构造函数, 修饰符 构造函数的参数列表
-可以获取Annotation中的信息,获取注解,获取注解名称,注解属性值
结论:
在运行期间,通过Class对象调用反射的api
可以反射实例化对象
可以反射访问属性,和反射调用方法
总之,编译期间能写的代码,用反射也能实现
介绍一些常规的api:
-反射方式创建对象
1.用无参数构造创建对象
Class对象.newInstance();//常用
2.用有参数构造创建对象
Class对象.getConstructor(new Class[]{若干参数的类类型}).newInstance(构造函数的参数);
比如:
//有参数构造
public User(String name,String password){
}
Class clazz=String.class;
Field[] Method[] Constructor[]
User user=new User("zhangsan","zs")
user.getClass().getConstructor(new Class[]{String.class,String.class}).newInstance("zhangsan","zs");
-反射方式获取Field中的信息
1.获取当前类以及长辈类的public Field
Filed[] fields=Class对象.getFields();
2.获取当前类中的所有的属性Field
Field[] fields=Class对象.getDeclaredFields()
3.获取当前类以及长辈类中指定的公有属性
Field field=Class对象.getField(String fieldName);
4.获取当前中指定的属性
Field field=Class对象.getDeclaredField(String fieldName);
5.通过反射设定Field属性的值
Field对象.set(Object obj,Object value);
如果Field是私有
必须先执行:Field对象.setAccessable(true);//设置属性可以访问
6.通过反射获取Field的值
Object value=Field对象.get(Object obj);
如果Field是私有
必须先执行:Field对象.setAccessable(true);//设置属性可以访问
-反射 方式获取Method方法信息
1.获取当前类以及长辈类的公有方法
Method[] methods=Class对象.getMethods();
2.获取当前类中的所有的方法
Method[] methods=Class对象.getDeclaredMethods();
3.获取当前类以及长辈类中的指定的公用的方法
Method method=Class对象.getMethod(String methodName,new Class[]{methodName的参数类型});
4.获取当前类中的指定的方法
Method method=Class对象.getDeclaredMethod(String methodName,new Class[]{methodNamed的参数类型});
5.通过反射动态调用指定Method
Object returnValue=Method对象.invoke(Object obj ,Object...args);
解析:
就是通过obj这个对象调用Method对象确定的方法,给这个方法
传递args参数,Method对象对应的方法的返回值是returnValue
-反射获取Constructor构造函数
查api文档
-反射获取Annotatioin注解
查api文档
在哪些地方使用反射,反射的应用场景
-用反射实现jdbc的通用查询和通用更新
-用反射解析注解
-单元测试,就是用反射实现的
-常见的框架,spring框架,springmvc框架,等其他的框架,都是用反射实现的
-EL表达式
等等
反射的优点:
大幅度提高开发效率,框架就是用反射实现的,框架提高了开发效率
反射的缺点:
反射的执行效率比非反射的方式执行效率低
反射可以 暴露类中的所有的细节,突破了封装.
注意:
第一套:
通用的对象的创建
通用生产对象的Factory
利用属性文件给Factory提供必要的信息(包名.类名)
第二套:
通用的setter方法和getter方法的调用
setMethod(String propertyName,Object obj,Object...args);
Object getMethod(String propertyName,Object obj);
利用属性文件或xml文件提供必要的信息
内省:就是自查的意思,本质就是反射,利用反射自省类中的属性,方法和其他
自省的实现方式有两种
方式一:
jdk(jre)中自带的一套自省的类库,类库包含的是api方法
侧重:属性和属性的值,以及属性所对应的gettter和setter方法
方式二:
apache基金会提供一套公共的自省类库Commons-BeanUtils.jar
注解:将来的发展趋势
用注解可以替换xml配置和属性文件的配置
用注解开发效率很高
注解实际就是一个标识,注解所代表的功能是用反射代码实现的
这段反射代码用于解析注解(找到,并确定注解存在),然后根据
注解设定的属性,来决定做什么样的功能
使用注解的步骤:
1.如何定义注解 创建注解
2.把注解应用在对应的目标上 把注解放在什么地方
3.通过反射api,解析注解, 有注解标识的就做对应的功能
1.如何定义注解
//代表此注解可以修饰的目标
@Target({ElementType.TYPE,ElementType.METHOD})
//此注解保留策略,在运行期间能使用此注解
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
//在注解中可以放置注解的属性,注解不是方法
public String value();
public String name() default "abc";
public String[] colors() default {"red","blue"};
}
元注解/源注解:用于修饰注解的注解,是jdk自带的注解
@Target(ElementType.XXX)//把注解应用在什么样的目标
@Target({ElementType.XXX,ElementType.XXX1})//把注解应用在什么样的目标
@Retention(RetentionPolicy.RUNTIME) //注解保留策略
元注解详解:
@Target(ElementType.METHOD):被修饰的注解能够在什么地方使用
(注解、类/接口、字段、方法、构造方法、参数上..)上面使用。
ElementType:
ANNOTATION_TYPE 注解类型声明
CONSTRUCTOR 构造方法声明
FIELD字段声明(包括枚举常量)
LOCAL_VARIABLE局部变量声明
METHOD 方法声明
PACKAGE 包声明
PARAMETER 参数声明
TYPE 类、接口(包括注释类型)或枚举声明
@Retention(RetentionPolicy.RUNTIME)
被它修饰的注解保留域。
编译器 类加载器 JVM
.java-> .class->加载->运行
SOURCE:源码级别,给编译器看的,
编译完之后直接丢弃该种策略的注解。
生成的.class字节码文件中没有该类型的注解
CLASS:给类加载器看的,
在类加载时可以做一系列引导操作。
编译器编译后存在,类加载器加载完之后,
丢弃该保留域的注解。
RUNTIME:给JVM看的,在程序运行的过程中做相关的
操作。编译器编译后保留,
类加载器加载后保留,
在JVM运行时通过反射技术反射使用的是
什么注解,注解属性的值是什么,
根据他们两个的值做对应的操作。
@Documented:自定义的注解如果使用Documented注解,
生成的doc文档上,使用该自定义注解的
地方会显示注解信息;
如果去掉@Documented,
生成的doc文档上将不再有注解信息。
注解中的属性详解
a.注解定义属性和接口中定义方法类似,缺省默认是public。
public 类型 属性名称();
b.定义属性时,如果没有使用default指定默认值,
使用当前注解必须为该属性赋值
(可以利用该特点,设置必须赋值的属性不要
使用default指定默认值)。
使用default指定默认之后,该属性在注解使用时可以赋值,
也可以不赋值。
c.注解中的属性类型是有要求的,
可以是八种基本数据类型,可以是枚举类型、
Class类型、String等,以及以上类型的“一维”数组
比如:String[] likes() default {};
在注解使用时,如果数组类型的属性的值是一个值得话,
赋值时可以把“{}”省略掉。@Anno1(name="zs",likes="fb")
d.存在一个特殊的属性:value,
如果只为该属性赋值,value=可以省略;
但是如果为注解中多个属性同时赋值的话,
value=是不可以省
2.把注解应用到对应的目标上
在对应的目标写
@注解的名称(属性名=属性值)
@注解的名称(属性名1=属性值1,属性名2=属性值2,....属性名n=属性值n)
3.反射注解
参见项目代码
总结:用注解就是根据注解和注解上的属性值,来决定是否做一些事
可以是指定的功能,也可以是调用一个具体方法
泛型:
一种参数化的类型
//非泛型 ,不是参数化类型
ArrayList list0=new ArrayList();
list0.add("abc");
list0.add(10);
//泛型标准做法 参数化类型, 参数是String类型
ArrayList<String> list1=new ArrayList<String>();
list1.add("abc");
list1.add("bcd");
//泛型,不推荐
ArrayList list2=new ArrayList<String>();
list2.add("abc");
list2.add("bcd");
//list2.add(10);
new Demo1().xxx(list2);
//泛型,不推荐
ArrayList<String> list3=new Demo1().yyy();
list3.add("abc");
list3.add("bcd");
//下列的做法是错误的做法,编译报错,两端的泛型必须类型一致
//ArrayList<Object> list4=new ArrayList<String>();
自定义泛型:
-类上的泛型,泛型类
泛型一定要先定义,后使用,在类的名字后面定义
泛型的类型一定要引用数据类型,不能基本数据类型
不能对确切的泛型类型使用instanceof关键字
比如:
//此种写法语法报错
if(num instacneof Generic<Number>){
}
定义在类上,在类的内部起作用,在整个类范围内类型一致
类上的泛型的具体类型需要在创建类的对象的时候指定泛型
的具体类型
如果在使用类上的泛型是不指定泛型的具体类型,默认的具体
的类型为泛型的上边界
-方法上的泛型,泛型方法
泛型要先定义后使用,在返回值之前定义,通常用一个大写字母
来定义一个泛型,可以定义多个,且定义在方法上,在方法内部
起作用,方法上的泛型,在方法被调用的时候自动推断出具体的
泛型,无论何时,尽量使用泛型方法,因为作用域小,只局限于方法
比如:
public <T> T save(T t){
return t;
}
<T>就是标志,标志此方法是一个泛型方法,放在修饰符和返回
类型之间,可以放置多个符号,<T,E>,说明有两个类型需要
参数化确定
泛型的上边界:
泛型所取的类型是上边界类或上边界类型的子孙类型
如果不指定泛型默认上边界是Object
泛型擦除:
泛型在编译期间起作用,真正执行期间,泛型已经被擦除了,
在编译阶段,编译器会将所有使用泛型的地方替换为该泛型的
上边界,并在必要的时候增加上必须的类型强制转和类型检查
所以,在执行期间没有泛型的概念
泛型在编译期间就被擦除掉了,此过程就叫做泛型擦除
jdk1.8的新特性:
2014年3月发布,其中提供很多的新的特性
-lambda表达式
-方法引用
-默认方法
-java跟javascript交互
-stream api 管道概念
-Date Time api,对java7以前的日期的加强
-Option类 专门用来解决空指针异常
-Base64类 专门用来对数据做编码和解码
等等...
1.lambda表达式:
lambda表达式可以称之为闭包,他是java8中的一个非常重要的特性
很多个用法都用到此表达式
lambda表达式允许把函数作为一个方法的参数,
函数作为参数进行传递到方法中
从而,使得代码更加简洁
语法:
(parameters)-> expression代码
或
(parameters)-> {statement代码块}
说明:
-可选参数声明: 不需要声明参数类型,编译器统一识别参数值
-可选参数圆括号:一个参数无须定义圆括号,多个参数需要定义圆括号
-可选大括号:如果主体包含了一个语句,就不需要使用大括号
-可选返回关键字:如果主体只有一个表达式返回值,则编译器会自动返回值
大括号需要指定返回了一个数据值
比如:
//不要参数,返回值为5
()->5
//接收一个参数(推断出数字类型),返回参数值的2倍
x->2*x
//接收两个参数(推断出数字类型),返回值为参数的差值
(x,y)->x-y
//接收两个参数,返回参数的差值
(int x,int y)->x-y
//接收一个参数,类型为String的参数,并输出参数的值
//推断出没有返回值,相当于返回值为void
(String str)->System.out.println(str)
其实lambad表示的本质就是接口的子实现
lambda表示简化了匿名内部类的写法
lambda的另一个书法叫做函数式编程
(因为在代码看不到类,只看到了方法/函数)
使用lambda表达式需要注意变量的作用域
-lambda表达式引用外层成员变量,
如果外层的成员变量为final的,则在lambda内部不能修改成员变量的值
如果外层的成员变量不是final,则在lambda内部能修改成员变量的值
-lambda表达式的局部变量可以不用声明为final,但是必须
不可以被后面的代码修改(即隐式的final特性)
-lambda表达式中不允许声明一个与局部变量同名的参数或局部变量
2.方法的引用
方法的引用方式是用 类名::方法名,其本质还是lambda表达式
具体的写法有4种
-构造器引用 语法Class::new
-静态方法引用 语法Class::静态方法的名称
-特定类任意的对象引用 语法 Class::非静态方法名称
-特定对象的方法引用 语法 instance对象::非静态的方法名称
代码参见项目
3.函数式接口
函数式接口(Functioninterface)就是一个有且只有一个抽象方法
但是可以有多个非抽象方法的接口
函数式接口,可以用lambda表达式,也可以用方法的引用
在jdk8之前已将有部分函数式接口
比如:
java.lang.Rnnable
java.util.Comparator
java.io.FileFilter
等...
在jdk8之后新添加的函数式接口
java.util.Function 其中包含了很多类
比如:
Consumer<T>
Predicate<T>
Supplier<T>
等...
4.默认方法:
在接口中可以放置有方法体的方法,但方法必须用default来修饰
在接口添加默认方法,是用来给所有的子类提供同一个功能
在接口中还可以放静态的方法,静态方法是有方法体的
5.Stream流管道
在java8中添加了一个新的抽象,称之为流Stream,可以让你以一种
声明式的方式处理数据
stream流处理方式,可以把要处理的元素看成是一种流,
流在管道中传输,并且可以在管道的节点上进行处理,
处理包含:筛选,排序,聚合等操作
元素流在管道中经过中间的操作的处理,最后由最终的操作得到前面的处理结果
Stream流管道的几个名词解释:
-数据源:流的源头,可以是集合,数组,IO,其他数据源
-聚合操作:就是一些在流中筛选数据的操作
比如:filter,map,reduce,find,match,sorted等...
-如何生成一个Stream流的源头
-stream() 生成一个串行流
-parallelStream() 生成一个并行.
比如:
List<String> strings=Arrays.asList("abc","bc","cba","qaz","qwe","");
List<String> filtered=strings.stream().filter(str->!str.isEmpty()).collect(Collections.toList());
-用stream流的forEach
forEach方法用来迭代六中的每个数据
Random random=new Random();
random.ints().limit(10).forEach(System.out::println);
-map方法用于映射每个元素对应的结果
List<Integer> numbers=Arrays.asList(3,2,2,3,7,3,5);
List<Integer> aquare8_5=numbers.stream().map((i)->i*i).distinct().collect(Collectors.toList());
System.out.println(aquare8_5);
-filter方法用于通过设置条件过滤出元素
List<String> strings=Arrays.asList("abc","bc","cba","qaz","qwe","");
long count8_1=strings.stream().filter((str)->str.isEmpty()).count();
System.out.println(count8_1);
-limit方法,用户获取指定数量的流
Random random=new Random();
random.ints(0,100).limit(10).sorted().forEach(System.out::println);
-sorted方法用户对流进行排序
Random random=new Random();
random.ints(0,100).limit(10).sorted().forEach(System.out::println);
-Collectors类实现了很多的归约操作
List<String> strings=Arrays.asList("abc","bc","cba","qaz","qwe","");
List<String> filtered8_3=strings.stream().filter(str->!str.isEmpty()).collect(Collectors.toList());
String merge8_4=strings.stream().filter(str->!str.isEmpty()).collect(Collectors.joining(","));
6.java8的日期操作
java8通过发布新的Date-Time api(JSR310)来进一步加强对日期与时间的处理
在旧版中日期和时间的api存在有很多问题
-非线程安全,java.util.Date是非线程安全的,所有的日期类都是
可变的.
-设计很差,java.util.Date跟java.sql.Date类都是日期类,重复定义很多
所以用Calendar来替代java.util.Date.
-时区处理很麻烦,在java.util.Date并没有提供时区处理的api
java.util.Calendar能够控制时区
java.util.TimeZone能够控制时区
在java8中有一个包java.time包提供了很多新的api
-Local(本地):简化了日期和时间的处理,没有时区的问题
-Zoned(时区):通过指定的时区处理日和时间
XML:扩展标记语言(Extensible Markup Languae)
是独立于软件和硬件的信息传输工具
xml可以简化数据共享
xml可以数据传输
xml可以平台变更
xml的处理指令
<?xml version="1.0" encoding="UTF-8" ?>
version:xml的版本
encoding:xml的内容编码
xml文档里包含的是xml的元素
xml元素:指的是从开始标签直到结束标签的部分,元素里可以
包含其他元素,元素也可以拥有其他属性.
比如:
<students>
<student id="1">
<name>张三</name>
<age>20</age>
<stuNo>S001</stuNo>
</student>
<student id="2">
<name>李四</name>
<age>30</age>
<stuNo>S002</stuNo>
</student>
</students>
students,student,name,age,stuNo这些都叫元素标签
也可以叫元素节点,元素对象,
id是某个元素的节点的属性,也可以叫属性节点,
也叫属性对象
张三,20,S001等这样的数据叫做值,也可以叫做文本节点
和文本对象
元素节点:一般用来表达某种语义
属性节点:一般用来修饰和补充元素节点
文本节点:一般用来表达指定语义的值
xml需要注意的几个问题:
1.xml元素节点,必须成对出现,且开始标签和结束标签名
必须一样
2.xml属性节点的值需要用引号括起来,可以是双引号也可以
是单引号,如果属性值本身包含双引,则需要用单引包围他
比如:<student name='二"狗"子'></student>
3.xml对大小写敏感,区分大小写
4.xml中要求,必须有跟元素节点,并且跟节点只有一个
根节点包含所有其他子节点(元素,文本,属性)
5.所有元素节点必须正确嵌套
6.实体引用:xml中有若干符号,是跟xml有冲突的,所以需要
用别名代替
实体引用 字符 说明
< < 小于
> > 大于
& & 与
&aops; ' 单引号
" " 双引号
比如: <name>1<2</name>
<desc>这个是一个"描述"文本</desc>
7.CDATA段,段中的内容当成一个文本块来看待
语法:
<![CDATA[文本内容]]>
比如:
<desc>
<![CDATA[
<说明>
CDATA段中的所有内容太都当成一个文本块来看待
如果有类似的元素节点也不会当成xml看待
只是普通的文本
</说明>
]]>
</desc>
xml解析:
SAX解析方式:(simple api for xml)是一种xml解析的替代
方法,想对比DOM解析,sax解析是一种速度快,更有效
的方法,因为他是逐行扫描xml文档,一边扫描,一边解析
而且相对于dom解析,sax可以在解析文档的任意时刻
停止解析
优点:解析可以立即开始,速度快,没有内存压力
缺点:不能对节点做修改
DOM解析方式:(document Object Model文档对象模型)
是w3c组织推荐的处理xml的一种方式
dom解析器在解析xml文档时,会把文档中的所有元素
按照其出现的层次关系,解析成一个Node对象(节点对象)
优点:把xml文件在内存中构建树形结构,可以遍历和修改
节点
缺点:如果文件比较大,内存有压力,解析时间会比较长
sax解析必须借助第三方工具dom4j,工具中提供若干jar包
jar包中提供了若干api,来解析xml,dom4j是一个非常
优秀的解析xml框架,也是一个开源的框架
Dom解析不需要额外第三方jar包,jdk(jre)提供了若干
api操作xml
SAX api:
-Element getRootElement(); Document类
获取根节点,以备获取跟节点后面的节点对象
-List elements(); Element类
获取当前节点下的所有元素节点,返回结果是List集合
-Attribute attribute(String attributeName); Element类
获取当前节点的某一个属性名对应的属性对象,
返回值是Attribute类的对象
-String getValue; Attribute类
获取当前属性对象中的属性值
-Element element(String elementName); Element类
获取 当前元素节点下的 指定元素节点名称的 元素对象
-String getText(); Element类
获取 当前元素节点里的 文本节点的 值
-String elementText(String elementName); Element类
获取 当前元素节点下的
指定元素节点名称的
节点里的
文本节点的 值
写xml节点到xml文件:
1.创建文档对象
Document doc =DocumentHelper.CreateDocument();
获取文档对象
Document doc=new SAXReader().read("xxx.xml");
2.定位节点 并添加
3.把document对象写到xml中
方式一:
XMLWriter writer=new XMLWriter();
OutPutStream os=new FileOutputStream("src/resources/new.xml");
writer.setOutputStream(os);
//把内存中的数据写入到new.xml文件中
writer.write(document);
writer.close();
方式二:
OutputFormat outformat=OutputFormat.createPrettyPrint();
outformat.setEncoding("UTF-8");
OutputStream os=new FileOutputStream("bin/resource/employee.xml");
XMLWriter writer=new XMLWriter(os,outformat);
//把内存中的数据写入employee.xml中
writer.write(document);
write.close();
sax方式写入xml常用api:
-Element addElement(String elementNodeName); Element类
在当前元素节点里,添加一个新元素节点
节点的名字elementNodeName
-Element addAttribute(String propertyName,
String propertyValue); Element类
在当前的节点上添加属性,属性的名称propertyName
属性的值 propertyValue
-Element addText(String text); Element类
给当前节点添加文本节点
XPath:路径表达式
xpath是一门在xml文档中查找信息的语言,xpath可用来
在xml文档中对元素或属性进行遍历
有了xpath就解决了逐层遍历
xpath是用网络路径表达式在xml文档中进行导航(快速查找)
xpath包含一个标准函数库
xpath是xslt中的主要元素
xpath是w3c标准
路径表达式:
-斜杠(/)作为路径的分隔符
-导航到同样一个节点,有相对路径和绝对路径两种
绝对路径:必须从"/"起始,后面紧跟节点
比如:/list/employee
相对路径:以当前路径作为起始点
比如: employee/name
-"."表示当前节点
-".."表示当前节点的父节点
-nodename(节点名称):表示该节点的所有子节点
-"/" 表示跟节点
-"//" 表示选择任意位置的某个节点
-"@" 表示选择某个属性
以下面xml的文档为例:
<?xml version="1.0" encoding="utf-8" ?>
<bookstore>
<book>
<title lang="eng">harry potter</title>
<price>39.9</price>
</book>
<book>
<title lang="eng">learning XML</title>
<price>59.9</price>
</book>
</bookstore>
/bookstore 选取跟节点bookstore,这是绝对路径
bookstore/book 选取所有属于bookstore的子元素book元素
相对路径
//book 选取所有book子元素,而不管他们在文档中的位置
bookstore//book 选取所有属于bookstore元素的后代的
book的元素
而不管他们位于bookstore之下什么位置
//@lang 选取所有名为lang的属性
谓语:
谓语条件,就是对路径表达式的附加条件
所有的条件,都写在[]中,表示对节点的进一步筛选
/bookstore/book[1] 表示选择bookstore的第一个book子元素
/bookstore/book[last()] 表示bookstore的最后一个book子元素
/bookstore/book[last()-1] 表示bookstore的倒数第二个book子元素
/bookstore/book[position()<3] 表示选择bookstore的
前两个book子元素
//title[@lang] 表示选择所有具有lang属性的title节点
//title[@lang='eng'] 表示所有具有lang属相,
且值等于eng的title节点
//bookstore/book[price] 表示选择bookstore的book子元素
且被选中的book子元素必须带有price子元素
/booksstore/book[price>35.0] 表示选择bookstore的
book子元素,且选中的book子元素的price
的子元素值必须大于35.0
/bookstore/book[price>35.0]/title
表示在上面的例子结果中选择title子元素
/bookstore/book/price[.>35.0]
表示选择值大于35.0的/bookstore/book的
price子元素
通配符:
* 表示匹配任何子元素
@ 表示匹配任何属性
node() 表示匹配任何类型节点
//* 选择文档中的所有元素节点
/*/* 表示选择所有第二层的元素节点
/bookstore/* 表示选择bookstore的所有子元素节点
//title[@*] 表示选择所有带有属性的title元素
要想使用xpath必须引入第三方的jar包
jaxen-xx-xx.jar
使用xpath的api
List SelectNodes(String xpath);
根据xpath的参数获取xpath指定节点的信息
java基础总结:
搭建环境
创建项目
基本语法
数据相关: 变量,数组
逻辑相关: 顺序结构,选择分支结构,循环结构
运算符 算术,逻辑,关系,赋值
面向对象:
封装:数据的安全,可以把多个不同数据类型的数据组织在一起
类,对象,内存存储
this() this.
继承:代码复用
组合
方法参数组合
类的成员变量组合
内存存储关系
super() super.
多态:
以长辈作为函数的参数
以长辈作为函数的返回值
抽象类,
接口
api:
常规api
集合api
ioapi
线程api
socket api
dom4japi
jdk1.5 jdk1.8新特性
杂项:
GC
单元测试
jar导入
异常
java的web开发:
web前端:
前端的技术:
html:用来做网页,用html构建网页的内容
css:用来修饰网页内容,给网页化妆,让网页的内容更美观
javascript:用户跟网页交互
js:做表单验证和网页特效
js:google,最早做了联想输入(局部刷新) 地图 用ajax技术实现
html:
html中有很多的标签/标记/对象/元素/节点
html应该遵守xml规范
xml中的节点是由程序员自定义,但html中的标记是固定
html中内容是由文本组成的,且都有默认样式,可以用css来改动
最终在浏览器中解析并在浏览器显示
是以.html或.htm为结尾
html的文档结构:
必须以<html>为跟节点
html节点有两个子节点<head>和<body>
<head> 放置的网页的头信息
<body> 放置的网页的内容信息,这些内容都显示在浏览器页面中
<html>节点的前面添加html文档声明
<!DOCTYPE> 用来声明html版本
现阶段常用的版本html4和html5
比如:
<!DOCTYPE html>
<html>
<head>
<!-- 告知网页的文字编码 utf-8 -->
<meta charset="UTF-8">
<!-- 网页的标题 -->
<title>Insert title here</title>
</head>
<body>
<!-- 文本内容 -->
</body>
</html>