1.*程序的数据操作往往都在内存中操作的,也就是说数据运算都在内存中完成.
2.*什么是变量?
--变量就是内存中的一块用来存放数据的存储单元.
--变量中的值可变
--我们通过变量名就可以找到内存中所开辟的存储单元.
--变量的数据类型有多个.
3.*如何在内存中开辟一个变量?
(1).要确定变量存放的数据类型
(2).确定变量所占内存空间的大小(确定数据类型)
语法: 数据类型 变量名;
比如:int num;
num = 10;
4.*两个变量中的值做交换,传统做法使用第三个临时变量做过度.
注意: 在一个方法中不能声明同名的变量,会冲突
补充:单行注释可以嵌套,多行注释不能嵌套
5.*标识符的命名规则?
什么是标识符?
--和名字有关的字符串,比如类名,变量名,常量名,接口名等.
类名的命名规则:
(1).首字符大写,第二个英文单词的首字符也大写,比如StudentClass 称之为"驼峰命名法"
(2).首字母必须是英文字母,下划线,$符号开头
(3).除了首字母以外的其余字符可以是英文字母,下划线,$符号,还可以是数字
(4).类名要通俗易懂,能一个单词就一个单词来描述
(5).避开关键字,就是java已经占用的单词
变量名的命名规则
(1).首字符小写,第二个英文单词的首字符也大写,比如myScore
(2).首字母必须是英文字母,下划线,$符号开头
(3).除了首字母以外的其余字符可以是英文字母,下划线,$符号,还可以是数字
4.变量名要通俗易懂,能一个单词就一个单词来描述,不要出现 int a = 10
5.避免使用关键字,就是java已经占用的单词,比如:int int; int class;
*计算机底层都是由0和1组成的二进制序列,0或1就代表了1位(bit)
计算机中的单位
1B = 8b (1个字节=8位)
1K = 1024B
1M = 1024K
1G = 1024M
*数据类型的作用有两个(*****)
--确定变量中存放数据的格式
--确定开辟内存空间的大小
*java中的数据类型(*****)
分两大类:
--基本数据类型
分为两类:
--数值型
--整数
byte(1个字节)
short(2个字节)
int(4个字节)(*****) 默认的整数都是int类型
long(8个字节)
--浮点数
float(单精度浮点数,4个字节)
double(双精度浮点数,8个字节)(*****) 默认小数都是double类型
--非数值型
--boolean(布尔类型,1个字节)只能存放true或false
--char(2个字节)--字符型,可以和整型互换,使用单引号括起来,
unicode编码表,兼容了前面的ascii表
--引用数据类型(后续章节讲)
--字符串 (String):使用双引号括起来的字符序列(*****)
--类
--接口
--数组
....
注意:字符串 + 任何数据类型都是字符串拼接,拼接好以后结果是字符串类型
*编码表:
就是把对应国家常用的字符对应的二进制码
常用的编码表:
ascii,iso-8859-1,gb2312,gbk,big5, unicode(全世界常用的字符编码表),utf-8
*常用的2个转义字符(*****)
\n:换行
\t:制表符
*java对数值的表示形式(了解)
1.十进制 0--9 满10进1
2.二进制 0,1组成 ,满2进1(*****)
3.八进制 0--7组成,满8进1
4.十六进制 0--9 A--F 满16进1(*****)
数制之间进行转换
十进制转二进制:除2取余,从后往前输出即可.
二进制转10进制:乘以2的n次方
100001 =1*2^0 +1*2^5=32+1=33
十进制转8进制:除8取余,从后往前输出即可.
反之:乘以8的n次方
十进制转十六进制:除16取余,从后往前输出即可.
反之:乘以16的n次方
16进制和二进制之间的互转
二进制转十六:4位变1位
比如:0010 0110 0101 0100== 2654
十六进制转二进制:1位变四位
0X1AFF=0001 1010 1111 1111
8进制和二进制之间的互转
二进制转八进制 :3位变1位
010 011 001 010 100= 023124
八进制转二进制:1位变三位
0357= 011 101 111
*基本的类型转换
--自动类型转换(隐式转换)
*数据类型兼容
*小的数据赋值给大的数据类型
--强制类型转换
*数据类型兼容
*大的数据赋值给小的数据类型,会报错,必须强制类型转换,可能会导致精度的丢失.
注意:
1. 一个表达式出来的结果往往跟大的数据类型走. long和float会跟float走,
数学运算表达式中有double的,结果肯定是double类型
2.假如表达式中有一个是字符串类型的数据,那么结果肯定是字符串类型.
*什么是表达式?
由操作数和操作符组成的就认为是表达式,比如 1 + 1, num1 - num2
*运算符
--算数运算符:
+(两个功能,数学运算,字符串连接),-(数学运算,负数),*,/(取商), % (取余数),
++:
单独的表达式++ 在前面和后面一样,都是自身加1.
int res = number++;//有其它运算符的时候,++ 在后面,先赋值,后自身加1
//int res = number; number++;
--:
和++操作方式一样.
--赋值运算符: =
复合赋值运算符:
+=,-=,*=,/=,%= 自身做对应的操作.
--关系运算符:--结果是true或false,出来的是boolean类型的数据
==,!=,>,<,>=,<=
--逻辑运算符:--结果是true或false,出来的是boolean类型的数据
(*****)&&:短路与: 多个条件只要有一个条件为false,那么整个表达式就为false,
只有所有的条件为true,那么表达式的结果就为true.
当第一个条件不成立了,后续的条件就不再做判断,直接结果为false
2>3 && 4>3
(*****) ||:短路或: 多个条件只要满足任意一个就为true,只有全部不成立才为false.
当第一个条件成立了,后面就不用再判断了,直接返回为true.
2<3 || 4>3
(*****)!:取反: !true !false
--------------以下主要是对二进制进行操作的----------------------
&: 与
*用在逻辑表达式中,不管第一个表达式1条件是否不成立,后续的表达式都会执行,不会短路
*4 & 5 =4 :两个都是1结果为1,其余结果都是0
|: 或
* *用在逻辑表达式中,不管第一个表达式1条件是否成立,后续的表达式都会执行,不会短路
4 | 5 =5
^: 异或
*当一个数连续异或两个相同数的时候又会得到自身的值.
--位运算符(用来操作二进制数据)
计算机底层使用一个数的补码来进行运算的.
计算机有三个码值:
原码:
反码:
补码:
正数的符号位是0,负数的符号位是1
正数的原码和反码和补码都是一样的,简称三码合一;
负数的反码= 正数的原码按位取反,符号位不变
负数的补码= 负数的反码 + 1;
补码= 原码取反(符号位不变) + 1///按位取反加一
(*****)<<:左移 --左移几位右边就是补多少个0,也就是乘以2的几次方.
5<<3 = 5 * 2^3
00000000000000000000000000101000
>>:右移--右移几位,左边补0,右边去掉几位,也就是除以2的几次方.
(*****)40>>3 =5 = 40 / 2^3
00000000000000000000000000000101
*运算符的优先级
搞不清楚就多添加小括号,小括号中还可以套小括号,小括号优先级最高.
-------------------------------------------------------------------------------
*获取键盘输入的整数,小数和字符串
1)导入Scanner工具:
import java.util.Scanner;
2)创建一个Scanner 对象
Scanner input = new Scanner(System.in);
3)调用Scanner对象中的方法来获取键盘输入的内容
int number = input.nextInt();//获取键盘输入的整数
double number = input.nextDouble();//获取键盘输入的浮点数
String number = input.next();//获取键盘输入的字符串
*程序默认是线性往下执行的,但在现实的环境中我们可能要选择性的来进行执行,所以要
学习java程序中的分支结构
--条件分支结构
1)简单if结构
语法:
if(条件){
//要执行的语句块
}
特点:条件成立执行if里面的语句块,条件不成立跳过if语句块,执行if后面的指令.
2)简单if...else
语法:
if(条件){
//if语句块
}else{
//else语句块
}
注意:当if和else中只有一条的语句的时候,花括号可以省略的,为了不出现问题,一般
都推荐使用花括号.
3)嵌套if
语法:
if(条件1){
if(条件2){
//语句块1
}else{
//else语句块1
}
}else{
if(条件2){
//语句块1
}else{
//else语句块1
}
}
4)多重if
语法:
if(条件1){
//语句1
}else if(条件2){
//语句2
}else if(条件3){
//语句3
}else if(条件4){
//语句4
}else{
//else语句块
}
注意:
1.当一个条件成立了,后面的判断就不会再执行了.
2.if条件假如互换的话可能会影响最终的结果
--循环结构
--------------
-----day3-----
--------------
5)switch...case语句
switch(表达式){
case 值1:
语句;
break;
case 值2:
语句;
break;
case 值3:
语句;
break;
...
[default:
语句;
break;]
}
总结:
1.switch 中的表达式出来的结果是一个常量值(整数(int,short,byte),字符(char), 字符串(jdk1.5版本后才可以使用),枚举 (enum))
2.break的作用是结束switch,可以省略,一旦省略了,从比对成功的位置开始一直往下执行,直到碰到break或swicth结束为止.
3. case后面列出的常量能否重复?不能重复.
4.只要每个case后面都有break,那么case顺序颠倒不影响结果
5.default也可以随意放,随意放的时候要注意是否加break;
6.和多重if进行比较,多重if的功能强大,它既可以做等值判断,也可以做区间判断,而switch只能做等值判断.做等值判断
switch语法更简洁.
补充一点:
比较两个字符串是否相等使用.equals("字符串"),不能使用==
比如: String str1 = "abc"; String str2 = "abc";
if(str1.equals(str2)){
}
Eclipse的使用
--它是一个开发平台,不仅仅是用来开发Java的,可以自定义在平台上安装相应的插件,就可以开发相应的应用.
--下载地址:www.eclipse.org
--必须前提配置好环境变量.
Eclipse开发java的步骤
1.创建一个java项目
2.创建一个class类文件
3.编写代码
4.运行字节码文件
Eclipse常用的设置
1.修改代码编辑区的字体大小
2.修改控制台的字体大小
Eclipse 常用的快捷键
1.ctrl+c:复制
2.ctrl+x:剪切
3.ctrl+v:粘贴
4.ctrl+z:撤销
5.ctrl+y:还原
6.ctrl+a:全选
-------------------
*ctrl+d:删除一行
*ctrl+/:单行注释的添加和去除
*ctrl + shift + / :添加多行注释 取消:ctrl+shift+\
*ctrl + shift + f: 格式化程序,必须在非中文的情况下
*ctrl + alt + 向下键 : 复制当前行到下一行
*ctrl + alt + 向上键 : 复制当前行到上一行
*alt + 向下键 或 向上键 :移动当前行前后移动
*ctrl + shift + o: 自动导包
*alt + /:自动猜测意图
sysout alt+/ 自动补全输出语句
main alt+/ 自动生成主方法
*切换工作目录 : file --->switch workspace
*项目的删除有两种方式:
-删除的时候不打勾,只是解除和软件的绑定,磁盘上没有删除
-删除的时候打勾,解除和软件的绑定,并从磁盘上永久删除
*学会导入java项目
import-->General -->Existing Projects into workspace
----------------------------------------------------------------------------------------------------
--循环结构
什么是循环?
---重复做类似的事情.
循环的好处的什么?
--对做重复的事情减少代码量
--做循环我们要明确几个步骤
1.确定什么情况下循环--确定循环条件
2.确定循环干什么事情--确定循环体语句(难点--要找出规律来)
3.确保防止死循环--更新循环体变量
--什么是循环体变量?
控制循环次数的变量就叫循环体变量,也就在循环的过程中必须要修改此变量,目的是让条件最终变为false停止循环
--循环的分类:
*while循环
语法:
while(条件表达式){
//循环体语句
}
执行流程: 当条件表达式为true的时候会执行循环体语句,执行完成以后再回到条件表达式来判断,
条件为true再执行循环体语句,直到条件表达式为false的时候才会结束循环.
特点:先判断后执行,循环体语句可能一次都不执行.
*do...while循环
语法:
do{
//循环体语句
}while(循环条件);--->分号不能忘
执行流程:先执行循环体语句,执行完再判断循环条件,要是为true,又执行循环体语句,直到循环条件为false为止.
特点:先执行再判断,循环体语句至少执行一次.
注意:
1.其实while和do...while 完全可以互相替换.根据实际情况选择
2.while和do...while 推荐在循环次数不确定的情况下使用.
*for循环
语法:
for(表达式1;表达式2;表达式3){
// 循环体语句4
}
表达式1:变量的初始化,只执行一次
表达式2:循环条件
表达式3:更新循环体变量防止死循环
执行流程:
表达式1-->表达式2(true)-->循环体语句4--->表达式3 --->表达式2(true)-->循环体语句4--->表达式3 --->表达式2 (false)-->循环结束
注意:
*表达式1可以省略的.--放到循环上面
*表达式2可以省略的.--代表条件永远为true
*表达式3可以省略的.--放在循环体语句之后
总结:3大表达式全都能省略,就是两个分号不能省略.
特点:和while一样.它是先判断后执行.推荐在循环次数确定的情况下使用for循环,因为语法更简洁.
其实3种循环完全可以互相替换.
int sum = 0;//求和
int number = 1;//变量初始化
while(number <= 100){
sum += number;
number += 3;//更新循环体变量
}
System.out.println("1+4+7+...=" + sum);
------
for(int sum = 0,number = 0;number <= 100;number += 3){
sum += number;
}
System.out.println("1+4+7+...=" + sum);
*变量的作用域
--变量的使用范围(变量的声明周期)
--块级变量,{ },在花括号中声明的变量,在花括号外面是访问不了的.
--------------
-----day4-----
--------------
二重循环:
--循环中嵌套循环称之为二重循环,三种循环中任意嵌套都属于两重循环
for(表达式1; 表达式2; 表达式3){ //外循环
for(表达式1; 表达式2; 表达式3){ //内循环
}
}
工作流程:当外循环条件成立后会执行内循环,内循环要循环完毕后再回到外循环.
假如外循环循环5次,内循环也循环5次,那么内循环中的循环体语句共
执行5* 5 = 25次.当外循环条件不成立的时候整个循环才结束.
二重循环可以用来打印几行几列的图形.纯粹用来学习.
*打印图形只要遵循这样的原则
外循环控制打印的行数
内循环控制每行打印的符号数
难点:每行打印的符号数和行数之间的关系.
*break和continue的用法
-break:往往配合switch 和 循环语句 使用
在循环中使用break直接中断循环,在switch...case语句中使用直接跳出switch.
break和if语句配合使用.
注意:break是结束最近包含它的那个循环.会改变循环的次数
-continue:
作用:结束本次循环而直接进入下一次循环,不会改变循环的次数,
只是少执行了一些指令而已.往往配合if语句使用.
*在循环中,有时学会设置标志位会使程序逻辑变得简单.
*什么数组?
--在内存中连续的一组具有相同数据类型的变量.
*如何声明数组?
语法:
先声明再创建数组
数据类型 [] 数组名;
数组名 = new 数据类型 [长度];
*数据类型分分类:
--8大基本数据类型
--引用数据类型
--String
--数组
* 基本类型和引用类型在内存中分配本质上有什么区别?
* JVM就把它看做是一台模拟的电脑,它模拟有运算器,寄存器,内存的功能.
我们在java中所声明的变量都是放在JVM所从物理内存上划过来的一块内存空间
这块内存空间它是怎么分配的?
它会把内存空间主要划分为以下几块:
1)栈内存: --以方法为一帧,里面存放的是局部变量,占据的内存比较少,会及时销毁
2)堆内存: --放的是对象(数组也一种),占据的内存比较大,不会及时销毁,
由垃圾回收器销毁.(面向对象再说)
3)方法区: --代码,常量,静态的内容等等.
总结:
1.方法中的基本数据类型只在栈内存中分配
2.方法中的数组它会在栈内存中和堆内存中都会分配.
栈内存中的变量放的是堆内存中的地址:--所以这个变量称之为"引用变量"
*获取数组的长度
数组引用名.length;
*如何访问数组中的元素,通过数组的下标访问,数组元素的下标从0开始,所以
最大的数组元素下标是数组的长度-1.
通过 数组名[下标]来访问
*数组中的元素是有默认值的
int:0
double:0.0
boolean:false
String:null
char:\u0000
*数组的赋值:
--静态赋值:--声明的同时赋值
int [] arr = {10,20,30}; -- 只能声明的同时赋值
int [] arr = new int[]{10,20,30}; --可以拆分为先声明后赋值.
强调:[]代表是数组的意思,可以放在变量名之前或之后,养成好习惯,一般写在前面
--动态赋值
Scanner input = new Scanner(System.in);
for(int i = 0; i < 3; i++){
score[i] = input.nextInt();
}
*如何遍历数组:
因为下标是从0开始,连续的,所以使用循环最方便
--------------
-----day5-----
--------------
*数组异常:
ArrayIndexOutOfBoundsException:数组下标越界异常
*数组的线性查找法
--从头扫描到尾,一个个拿出来比较,往往对无序的数组来进行查找使用.
*冒泡排序算法:
--小的网上冒,大的往下沉.
原理:n个数比较n-1轮,每一轮找出一个最大的值往下沉,每一轮(i=0)又比较n-1-i
总结:
*外循环控制比较的轮数,n个数总共比较n-1轮
*内循环控制每轮比较的次数,假如i代表轮数,从0开始的话,每轮比较的次数
是n-1-i次(关系)
*每轮比较的做的事情是两两比较小靠前.
*选择排序法:
原理:
第一轮找出最小的那个数的下标,看是否在下标0的位置,不是的话交换
第二轮找出未排序中最小的那个数的下标,看是否在下标1的位置,不是的话交换
...以此类推
N个数总共比较了n-1轮.
*二分查找法
前提:数组中的值必须是排好序的.
原理:先获取数组中间那个元素和要查找的元素比,假如比要查找的元素小,就扔掉左表
起始下标=中间下标+1,假如比要查找的元素大,就扔掉右边,终止下标=中间下标-1
当起始下标大于终止下标的时候,代表查找的元素不存在.
*二维数组 (了解)
--数组中的每一个元素又是一个数组,称之为"数组的数组"-->二维数组
*二维数组的声明
语法:
数据类型 [][] 数组名 = new 数据类型 [长度][可以指定也可以不指定];
声明的同时对二维数组做初始化
int [][] arr ={{1,2,3},{4,5,6},{7,8,9}};
注意:
第一个[]代表的是一维数组的长度,必须指定.因为二维数组的大小是由
第一维确定的.
*如何遍历二维数组中的数据
使用二重循环
外循环遍历的是第一维数组
内循环遍历的是第二维数组
---------------------------------------------------------------------------
*java中的静态方法的使用
什么是方法?方法的作用是什么?
--方法内封装了多条指令,主要就是完成一个功能模块,可以被重用.
--除了main方法由JVM自动调用以外,其余的方法都要去调用才会执行
静态方法的语法:
public static 返回类型 方法名 (参数列表){
//指令的集合
}
方法的分类:(*****)
1.返回值为void(空)的无参数的方法
public static void show(){
}
2.有返回值的无参数方法
public static 返回类型(数据类型) show(){
return 值;
}
强调:
1.返回的值必须和设置的返回数据类型一致.
2.只要方法声明是带返回值的,必须要有return返回.
3.return 的作用:返回相应的值并结束方法
3.带返回值且带参数的方法
public static 返回类型 方法名(参数列表){
return "值"
}
public static String getSmoking(int money,String car){
if(money > 65){
return "开着"+car +"去买了软中华";
}else{
return "开着"+car +"去买了大前门";
}
}
//方法中声明用来接收数据的变量我们称之为"形参"
String str = getSmoking(money, car);
调用方法中的参数我们称之为"实参".
4.不带返回值且带参数的方法
public static void 方法名(参数列表){
}
总结一点:一块在堆中分配的空间可以同时被多个应用变量所引用.
--------------
-----day6-----
--------------
没有时间忘记看了!!!
--------------
----day7am----
--------------
4.面向对象编程的特性
万物皆对象--实实在在存在的物体.
程序是一组对象彼此之间在发送消息--程序由多个对象之间互相调用功能来完成的
每个对象都有自己的内存占用,可以组装成更大对象
每个对象都有类型,特定类型的所有对象可以接收相同消息
==>每个相同类型的对象都有相同的功能(方法)
5.类和对象的区别和关系
类:是一个抽象的概念,比如人类,没有具体要具体的个体.
它是一类具有相同特征和行为的对象的抽象.可以把它认为是模板,图纸.
类是对象的一个数据类型,也可以认为类是我们程序员自定义的一个复杂的数据类型
对象:是具体的概念,它是根据类构造出来的具体的物体.
6.对象由特征(名词,比如姓名,年龄等)和行为(动词,比如,吃跑)等组成.
7.类是有个抽象的过程,什么是抽象?
就是把一组特定类型对象中我们所要关注的主要细节进行抽取出来,而忽略哪些次要的
细节,这就是抽象,所以设计类其实就是一个抽象的过程.
8.如何编写一个类
在程序中,属性(字段)就代表了特征,方法就代表了行为
语法:
class 类名{
属性1;
属性2;
方法1()
方法2()
}
类是一个封装的过程,就是把我们抽象出来的属性和方法放到一个类中.
在程序中属性就是声明变量,行为就是声明方法
--------------
----day7pm----
--------------
*创建对象
-使用new关键字
Student stu = new Student();//构建了一个学生对象
创建过程:
1.先在栈中创建一个Student类型的局部变量stu
2.在堆中开辟一个Student类型的对象
3.对对象的属性赋予初始值
4.调用构造函数来对属性做初始化--后续再说
5.把对象的地址返回给stu这个引用变量
*如何访问对象的属性和调用调用对象的方法.
通过"."运算符来访问.
语法:
对象名.属性名
对象名.方法名
强调:
1.对象的属性都是有默认值的,String:null,int:0,double:0.0 boolean:false,char:'\u0000'
2.每一个对象都有各自的一份属性,互不干扰.
3.对象中的方法是当类加载的时候就会存在方法区,只有一份,此类构造出来的所有对象
共享这一份方法.
4.当前对象:--当前你引用的那个对象,关键字this(当前对象的地址),我们把
this就认为是一个变量,保存了当前对象的地址
//创建一个Teacher对象,(包含三个属性,姓名,年龄,薪水),写一个方法,
要求输出Teacher对象中属性内容:"老师的名字叫chenhao,今年36岁,薪水:1000"
构造两个不同的老师出来:
*方法的声明
public 返回类型 方法名(参数列表){
//方法体
}
方法签名: 方法名+参数列表,java方法名是否冲突是根据方法签名来定的.
也就是说方法名一样,参数列表不一样,这两个方法是不冲突的.只有方法名
一样,参数列表也一样,那么认为这两个方法是同名方法,冲突.
*变量的作用域:
--变量声明的位置决定变量的作用域
--变量作用域确定可在程序中按变量名访问该变量的区域
--块级局部变量:声明在{}内的变量,它的作用域只能在{}内访问
--方法局部变量:声明在方法中的变量,它的作用域只能在方法中使用,出了方法就不存在了
--属性:它能给对象中的所有方法都能访问.
http://cloud.oracleoaec.com
拿你们账号登录即可考试
考试过程中浏览器别关闭.
--------------
-----day8-----
--------------
*String类型--代表字符串的意思,它是引用数据类型
注意:字符串是常量,一旦声明了就不能改变了.
*字符串的声明方式
1.String str = "abc";
2.String str = new String("abc");
这两种声明方式的区别?
String str = "abc";
它会在常量池去找有没有abc这个字符串对象,假如有,就直接把对象的地址赋给变量
没有就在常量池中创建这个对象.
String str = new String("abc");
不管有没有存在abc,它都会在内存中重新开辟一块新的空间来保存字符串
总结:equals方法比较的是字符串的内容是否相同,==比较的是两个字符串的地址是否一样.
*字符串的常用方法
length()--返回此字符串的长度。
charAt(int index)--返回指定索引处的 char 值。(*****)
concat(String str)--将指定字符串连接到此字符串的结尾。
contains(CharSequence s) –是否包含指定的字符串序列,返回true或false
equals(Object anObject) --将此字符串与指定的对象比较
compareTo(String anotherString) –按字典顺序比较两个字符串,相等返回0.
假如小于anotherString,则返回小于0的数,大于的话,返回大于0的数 (*****)
indexOf(String ch) --返回指定字符在此字符串中第一次出现处的索引。(*****)
indexOf(String ch,int fromindex) --返回从指定位置开始往后找指定字符在此字符串中第一次出现处的索引。
找不到返回-1
split(String regex) -- 根据匹配给定的正则表达式来拆分此字符串。
substring(int beginIndex) –截取字符串(*****)
toLower/upperCase() –将指定字符串进行大小写转换
trim()--去除字符串的两端空格(*****)
String.valueOf() --把指定的数据转换成字符串(*****)
endsWith()--判断字符串以什么结尾
startsWith()--判断字符串以什么结尾
-----------------------------------------------------------
1.输入自己的身份证号码,并由此号码输出自己的生日,年月日
2.统计"朋友"这首歌中共出现"朋友"多少次
indexOf("str",index)
*NullPoniterException 空指针异常
什么情况下产生?
当对象没产生的时候就去调用对象中的方法.
*Java程序调试(debug)--单步调试
--主要解决的是逻辑问题.
*单步调试的步骤
1.猜测大体出错的位置.
2.设置断点--让程序在断点的位置进行挂起.(双击添加或删除断点,可以设置多个断点)
3.单步运行查看执行的流程和变量中值的变化是否符合我们的预期
F6:进入下一条指令(单步)
F5:进入方法
F8:接着往下执行,直接跳转到下一个断点位置,假如下一个断点没有,直接程序运行完毕.
4.找出错误,修改并重新运行
-----------------------------------------------------------------------------
封装:
1. 抽象出属性和方法放在一起,以类的形式呈现.
2. 信息的隐藏,如何实现信息隐藏呢---通过访问修饰符(public,默认的(不写),private ,protected)
通过访问修饰符我们可以选择性的对属性和方法对外公开.
*访问修饰符:
public:权限最大,可以被外面的任意对象访问其修饰的属性和方法
private:权限最小.外面所有的其它对象都不能访问其修饰的私有实行和方法
只能给本对象内部使用.
*对属性的封装
属性私有化,对外提供与之对应的get和set方法来对属性进行读写操作.
设置get和set的方法的好处,可以在内部书写想用的逻辑来控制数据的准确性.
*对方法的封装
大多数情况都使用public,对外提供调用,也可以使用private来修饰,使用
private修饰的方法只能被本类的其它方法调用,只服务于本类,称之为"帮助方法".
*构造方法(*****)
--作用:在创建对象的时候对属性做初始化的工作.
构造方法和普通方法的区别?
1)语法上的区别:
构造方法语法:
-访问修饰符 方法名(参数列表)
记住:方法没有返回类型且方法名和类名必须一致.
2)调用上的区别:
构造方法只能在构造对象的时候去调用,而普通方法想调用的时候就去调用.
总结:
1.一个类我们没有显式的去声明一个构造函数,那么这个类默认会有一个
无参数的构造函数,一旦显式声明了一个构造函数,那么默认无参数的构造
函数就不存在了.
2.构造函数是在构造对象的时候初始化的,而属性对应的set方法是用来对属性进行
修改的.
*对象创建的过程
1)在栈中声明一个引用变量
2)在堆中创建一个对象
3)对对象的属性赋予默认值
4)对对象的属性赋予给定的初始值
5)调用构造函数来对属性做初始化
6)把对象的地址赋值给引用变量.
*构造方法的重载--方法是由方法签名
1.方法名相同
2.参数列表不同(参数的个数不同,参数的数据类型不同)
*在构造函数中去调用另一个构造函数可以通过this(参数列表)来调用.
切记必须作为构造函数中的第一条指令存在.
--------------
-----day9-----
--------------
*static关键字(静态)
--可以用来修饰属性: 静态成员变量(属性,字段,成员变量)
--可以用来修饰方法: 静态方法
*static修饰的属性在什么时候加载 ?
当一个类(.class)被加载到JVM的时候,static修饰的属性就会在方法区分配空间.
它优先于对象的创建(存在).
static修饰的属性在内存中只创建一份,被此类所构建出来的对象共享.所以我们认为
static属性不属于任何对象,它属于类的,所以也可以称之为类变量
访问静态属性和静态方法的时候推荐使用类名.属性和类名.方法名()
特点:
1.内存中只有一份,对象共享
2.优先于对象的创建
3.属于类,通过类名.静态属性或类名.静态方法来调用
*代码块
{
指令
name = "aa"
}
特点:
1.代码块优先于构造函数执行,可以用来对属性做初始化的工作,不够灵活
2.和构造函数一样,构造一个对象就会执行一次.
*静态代码块
static{
//指令
}
特点:
1. 只会执行一次,当类被加载的时候就会执行,优先于构造函数和代码块
2. 静态块给静态变量做初始化工作的.
总结:
1.静态方法中只能访问外边的静态的内容(静态的只能访问静态的)
2.非静态的方法可以访问外边的静态的和非静态的元素.
原因:静态的优先于对象的创建.
3. 只有共享的内容才考虑设置成static的.
*设计模式
--单例模式(SingleTon)
--一个类只能实例化一个对象供外部其它的类来使用.
--单例模式要点
某个类只能有一个实例
它必须自行创建这个实例(外面的类不能实例化对象)
它必须自行向整个系统提供这个实例
--饿汉式单例模式(不管使用不适用这个内部创建的对象,对象都已经提前创建完了)
1)构造函数私有化
2)内部创建一个静态的对象
3)提供一个静态的方法对外提供内部所创建出来的唯一的对象
特点:只要用到这个类就会帮你创建对象,用不用都会产生对象.
--懒汉式单例模式(只有在使用的时候帮你创建对象)
1)构造函数私有化
2)先声明一个静态的引用变量
3)在静态方法内部去实例化这个对象.
----------------------------------------------------------------------
*为什么要有方法重载?
就是解决方法名膨胀的问题.
*方法重载?(Overload)
1.方法名相同
2.参数列表不同(参数的个数和参数的数据类型不同)
*JVM判断方法是否冲突是根据方法签名(方法名+参数列表)来定的.
*package(包)
-作用:
1.分门别类存放类,易于查找和维护
2.防止类的命名冲突
3.包也是一种访问权限.
-如何进行对类打包
使用package 关键字
包名的规范:--往往是唯一的.一般以公司域名的倒置来命名 比如:com.oracleoaec.dao
养成好习惯,至少两级包名
有两种方式创建包
1)先创建包,再包里创建类
2)创建类的同时创建包
跨包访问有两种方式
1) 全局路径(绝对路径)的方式(包名.类名)
2) 使用import 先导入 再使用,比较方便
package 必须必须放在第一条指令 在import之前
/*
练习:
一个老师(姓名,年龄,引导学员自我介绍),老师有多个学生(姓名,年龄,自我介绍)5个学生
1.老师让所有的学生都完成自我介绍的功能
2.老师让引导指定的学员自我介绍(输入学生的名字,
根据输入的名字让此学员自我介绍,找不到显示
没有此学员信息)
3.学生和老师类放在不同包中
有一个主人(Master类),
他养了两条狗(Dog类),一条名字叫“旺财”,
另一条叫"小强",狗有共同的方法"叫",和"要尾巴"
现在提供一种食物(Food)--包含名字
主人分别给两条狗喂食物,两只狗厌食,
狗只吃骨头的时候摇尾巴(如果主人为狗吃别的食物,
就显示“狗不吃某某食物”并叫)
--------------
-----day10----
--------------
继承?
子类继承父类(超类,基类)
*特点:
1.要符合is a 的关系,也就是子类 is a 父类
2.父类更通用抽象,子类更具体
3.子类继承父类的非私有属性和非私有方法,并可以在其基础上扩展自己特有的属性和方法.
4.Object类是所有类的顶层父类,所有的类都直接或间接的继承了Object类
*作用:
1.对父类代码的重用
2.实现多态的一个前提.(父类的引用变量可以引用其子类对象)
*程序中如何实现继承?
使用关键字extends来实现继承,一个类只能继承一个父类.
*super 关键字 和 this 关键字进行区分.
this :它就是一个变量,保存的是当前对象的引用地址
super:它就是一个变量,保存的是父类对象的引用地址,
每个子类对象内部都持有父类的引用,就是super
this(参数列表)--调用本类的无/有参构造函数
super(参数列表)--调用其父类的无/有参构造函数
总结:
1.当我们创建子类对象的时候默认会调用父类无参数的构造函数来先创建父类的对象.
2.当我们创建子类对象的时候势必先调用父类的构造函数来创建父类的对象.否则无法
创建子类对象.
3.调用父类构造函数必须作为子类构造函数中的第一条指令.
*方法重写(override)?--多态的一个前提
--子类方法对父类方法的改写(屏蔽),子类对象会调用子类重写父类的那个方法,而不是继承来的方法.
*什么情况下会对父类的方法重写?
1.子类的方法和父类的方法完全一样(*****)
2.子类重写父类方法的访问修饰符权限要大于等于被重写方法的访问修饰符
3.父类私有方法是不能被重写的
*override和overload的区别?(面试题)
override:子父类的关系,子类对父类方法的改写,多态的一个条件.
overload:同个类中同名不同参的方法,对方法名进行复用
*final关键字(常量)
1.修饰变量变成了常量,一旦赋值了就不能再修改
常量属性只能在属性中初始化或在构造方法中初始化,养成好习惯,常量名都大写.
常量存在目的就是让程序员去直接调用,所以一般声明常量都和
public static final 配合使用
2.final修饰方法的话代表此方法不能被重写.
3.final修饰类的话,此类不能被继承.
注意:静态方法不叫重写,因为它不属于对象,是属于类的
*Object类--它是所有类的根类,所有的类都直接或间接的继承了它.
--x.equals(y)方法,默认的功能是x和y是否引用的是同一个对象,是的话返回为true,否则返回false
--假如我要用自己的比较规则,那么我们有必要去重写Object中的equals方法,比较规则自己定,我们可以使用eclipse自动
帮我们重写equals方法.
--toString()方法默认的格式是 包名.类名@地址
我们可以对其进行输出格式的改写.
----------------------------------------------------------------------------
什么是多态?
--多态性是指同一操作作用于某一类对象,可以有不同的解释,产生不同的执行结果。
实现多态使用到的技能点(知识点):
1.父类的引用变量可以引用子类的对象(把子类赋值给父类)--上转型
2.方法的重写
3.继承
多态的作用
多态通过分离做什么和怎么做,从另一个角度将接口和实现进行分离
“多态”则消除了类型之间的耦合关系
多态的存在提高了程序的扩展性和后期的可维护性
*重要的一个设计原则:"开闭原则"
--对扩展是开放的,对修改是封闭
*对象的转型:
1.上转型--子类转换成父类,总是安全的,(子类就是父类)但会丢失方法
(父类的引用变量引用子类的对象,父类调用自身有的方法或子类重写父类的方法,
子类特有的方法无法调用)
2. 下转型--父类转换成子类对象
父类型转换成子类型可能存在安全隐患,编译的时候不会报错,但运行的时候可能
会出现转换异常(classCastException).下转型是不安全的.
*instanceof操作符 --判断一个对象是什么类型,是的话返回true,不是的话返回false,
下转型的时候我们可以用来先做判断再转换
*静态绑定--在编译的时候确定方法是属于谁的,比如静态方法
*动态绑定--是在运行的过程中,根据实际传过来的对象来确定到底调用哪个对象中的方法
(此方法是重写父类的方法)
尽量在设计程序的上层要依赖抽象层,底层也要依赖抽象层,这样程序就比较灵活,扩展性好
--------------
-----day11----
--------------
*抽象方法
--特点:
1.只有方法头,没有方法体的方法,也就是说只有方法的声明,没有方法具体的实现.
2.抽象方法使用abstract关键字来进行修饰.
*什么情况下使用抽象方法.
父类的方法本身实现没有意义,只是起到一个声明的作用,主要是让子类去重写这个抽象方法.
*抽象类
特点:
1.当一个类中只要有一个抽象方法,那个这个类一定是抽象类
2.抽象类本身是不能实例化的(抽象的东西都是不能实例化的),它是依附在子类上的.
当子类实例化的时候它会创建对象
3.抽象类中可以有抽象方法,也可以没有抽象方法,抽象类中也可以有属性.但只要有
抽象方法的那它肯定是抽象类.
4.一个抽象类中的抽象方法要么子类直接去实现,要么子类继续抽象下去让下一代去实现.
*设计模式2: 模板方法(了解)
定义: 一个模板方法用一些抽象的操作定义一个算法,
而子类将重定义这些操作以提供具体行为
意图:定义了在一个操作中的一个算法框架,把一些步骤推迟到子类去实现。
模板方法模式让子类不需要改变算法结构而重新定义特定的算法步骤
*接口(interface)
语法:
interface 接口名{
}
特点:
1.接口不能实例化
2.只能放公开的静态常量,不能放普通的属性
3.接口中只能放抽象方法,不能放带有方法体的方法
4.一个类可以实现多个接口,一个类只能继承父类
5.接口可以继承接口,使用extends关键字
6.一个类可以使用implements关键字去实现接口,可以同时时间多个,用逗号隔开.
7. 实现接口的类必须去重写接口中的抽象方法或者继续抽象下去.
也可以这么说:接口可以认为是纯抽象类
强调一点:常量名要大些,多个单词组成的常量名用_隔开,比如MAX_AGE
*接口的作用?
--它是一种功能的扩展,关注的是不同类型对象之间共同的能力.
interface 厨师的能力{
//做菜有品相
//做菜美味
//刀工好
}
interface 赛车手 {
//不怕死
//会飘移动
//反应能力快
}
class Teacher implements 厨师的能力,赛车手{
}
class Doctor implements 厨师的能力{
}
------------------------------------------------------------------
接口的主要目的是使不同类型的对象只要方法(能力)相同,同样能实现多态
对象之间的关系
1.is a (继承)
2.has a (组合)
3.like a (实现接口) ---关注的是不同类型对象之间共同的方法.
实现多态的三种方式:
1.使用普通的父类去接收不同的子类对象(用的比较少)
2.使用抽象父类去接收不同的子类对象,子类对象对父类中的抽象方法有不同的具体实现.
关注的是is a 的关系(*****)
3.使用接口去接收不同的实现类对象,实现类对接口中的抽象方法有不同的具体实现.
关注的是不同对象之间公共的能力(行为).
什么时候使用抽象类?什么时候使用接口(面试题)
--当子类要继承父类中的属性和方法的时候并且符合is a 的关系,就使用抽象类
--当不同的实现类之间找不到共同的父类,但能找到共同的行为,那么我们可以使用
接口来实现多态.
访问修饰符:
public: 本类和外部的其它类都能访问
private: 只能本类访问,外面其它的类不能访问
默认的: 同个包中的其它类能访问,不同包中的其它类不能访问
protected: 同个包中的其它类可以访问,不同包中的子类也可以访问
public>protected>默认的>private
--------------
-----day12-----
--------------
*简单工厂模式(不属于23种设计模式的一种,它是工厂模式的一种最简单的)
定义:专门定义一个类来负责创建其他类的实例,被创建的实例通常都具有
共同的父类或接口。
意图:提供一个类,由它负责根据一定的条件创建某一具体类的实例
优点: 解决了职责明确的问题,解耦
缺点:当增加产品的时候,势必会修改工厂类的判断逻辑,违反了"开闭原则".
所以什么情况下使用简单工厂?
产品数量比较少或者产品固定的情况下才使用简单工厂.
*工厂方法设计模式
一个产品对应一个工厂.
优点:不会违反开闭原则.
缺点:扩产一个新产品就要扩展一个具体的新工厂,工厂类极具膨胀.
*抽象工厂(自己学一下)
*策略模式:
定义:是对算法的包装,把使用算法的责任和算法本身分割开,委派给不同的对象管理。
策略模式通常把一个系列的算法包装到一系列的策略类里面,
作为一个抽象策略类型的子类型。
就是:"准备一组算法,并将每一个算法封装起来,使得它们可以互换。"
意图:针对一组算法,将每一个算法封装到具有共同接口的独立的类中,
从而使得它们可以相互替换。策略模式使得算法可以在不影响到客户端的情
况下发生变化。
*内部类:
--类的内部嵌套了另一个类,此类就叫"内部类"
*内部类的特点:
1.内部类可以使用4个访问修饰符来修饰内部类,起到对内部类隐藏的作用
2.内部类可以直接访问外部类的所有成员,包括私有的成员。
3.外部类不能直接访问内部类的成员,必须首先要建立内部类的对象才可访问。
4.内部类可以解决一些问题,比如间接地去实现多继承。
可以避免修改接口而实现同一个类中两种同名方法的调用。
*内部类的分类:
1.成员内部类
特点:
1.创建非私有的内部类对象的语法: Outer.Inner inner = new Outer().new Inner();
2.内部类可以访问外部类的所有属性和方法.假如内部类和外部类的属性和方法
冲突的话,会使用就近原则使用自己内部类的属性或方法.
3.我们可以把成员内部类看做是外部类中的一个成员变量或成员方法
4.要在内部类中访问外部类的属性或方法,通过"外部类.this.属性或方法"
5.内部类所编译成的class文件的名字"外部类名字$内部类名.class"
6.成员内部类中不能有静态属性或静态方法,但可以是静态常量.
2.静态内部类
特点:
1.使用static来修饰的内部类,当使用外部类的时候,内部类就已经加载了.
2.我们可以把静态内部类看成外部类中一个静态方法,它内部职能访问外部类
静态的内容.
3.在外部其它类中创建静态内部类对象的语法:
外部类.内部类 变量名 = new 外部类.内部类构造函数;
4.外部类可以访问内部类中的静态属性和静态方法,通过内部类名.静态属性或静态方法.
3.匿名内部类
特点:
1.没有类名的内部类.
2.我们通过new 父类(抽象类){} 或 new 接口(){} 来构造出父类的子类对象或
接口的实现类对象.
语法:
new 父类/抽象类(){
//重写父类中的方法或抽象类中的抽象方法
};
new 接口(){
//重写接口中的抽象方法
};
new出来的是父类的子类对象或接口的实现类对象.
什么时候使用:
当一个对象只用一次的时候我们可以考虑匿名内部类对象
4.局部内部类
--声明在方法中的内部类就局部内部类
特点:
1.我们把局部内部类看作是方法中的局部变量,不能使用访问修饰符.
2.在外面其它的类中是不能实例化局部内部类对象的,只能在声明其方法中
去创建局部内部类对象
说明:jdk1.7之前的版本(包括1.7)局部内部类中只能访问其声明方法中的常量.
jdk1.8后修复了这个问题.
*自动装箱和拆箱(jdk1.5开始有的)
基本数据类型都有与之对应的包装类(引用数据类型)
int -- Integer
char --Character
double-- Double
short -- Short
boolean --Boolean
long -- Long
byte - Byte
float -Float
自动装箱 :
把基本数据类型赋值给引用数据类型,基本类型会先自动包装成引用数据类型.
自动拆箱:
1.把引用数据类型赋值给基本类型的变量,比如Integer会默认调用intValue()
1.当引用数据类型和基本类型做运算的时候,先拆箱成基本类型做运算,运算好
以后再看赋值给哪个类型的变量,选择是否装箱
*枚举:(enum)
--列出选项让对象选择(清单列表)
--预先创建出来多个对象供外面其它的对象来选择使用.
使用enmu关键字来创建枚举类
switch 可以匹配枚举类型,因为枚举类型也是常量.
--------------
-----day13-----
--------------
String:它是常量,频繁的修改字符串会频繁的在内存中开辟新的空间,效率比较低
所以假如我们想频繁的修改字符串如何提高效率,最好就是修改的是同一块内存空间,
我不要重新频繁的创建的新的空间
StringBuffer和StringBuilder来解决效率的问题.
相同点:构造函数和功能都一样.
区别:StringBuilder是线程不安全的,StringBuffer是线程安全的
StringBulider从单线程的角度来说效率高,多线程的时候我们也可以自己加锁同步.
所以推荐使用StringBulider
如何使用StringBuilder?
--使用构造函数来构造对象
*StringBuilder原理?--动态字符串,不要考虑数组越界的问题.
假如使用无参构造函数去创建一个StringBuilder对象,其实底层是开辟了一个长度
为16的字符数组,调用append方法无非就是给字符数组赋值,当添加的数据超过字符数组
长度的时候,会自动扩容,新建一个新的字符数组,长度为原来字符数组大小的2倍加2.
最后把原先字符数组中的数据拷贝到新的字符数组中.
*有什么功能--自己看api文档.
*常用类
*Date
Date date = new Date();
date.getTime();//得到当前系统时间的毫秒数.
*DateFormat类它是一个抽象类.
我们可以使用它的子类对象来格式化日期
1.把日期对象格式(Date)化成指定格式的字符串 format(Date date)
2.把日期格式的字符串转换成Date对象 parse(String date)
(了解)
DateFormat df = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT, Locale.CHINA);
System.out.println(df.format(new Date()));
以上还是按照预先默认设定好的格式供我来格式化日期时间,还是不够灵活.
最好就是按照我们自定义的格式来格式化日期时间.
*SimpleDateFormat(*****)--自定义日期时间格式化
SimpleDateFormat(String pattern)
用给定的模式和默认语言环境的日期格式符号构造 SimpleDateFormat。
记住常用的占位符:
yyyy:年
MM:月
dd:日
hh:小时
mm:分钟
ss:秒
SS:毫秒
//把日期对象转换成指定格式的字符串
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
System.out.println(sdf.format(new Date()));
//把指定格式的日期字符串转换成Date对象
String str = "2017年08-01 12:33:32";
SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM-dd hh:mm:ss");
try {
Date date = sdf.parse(str);
System.out.println(date);
} catch (ParseException e) {
System.out.println("转换失败");
e.printStackTrace();
}
注意:格式必须要一致,不然转换失败
*Calendar(日历对象)--取代了Date对象
Calendar calendar = Calendar.getInstance();
// System.out.println(calendar.toString());
calendar.set(Calendar.YEAR, 2011);
calendar.set(Calendar.HOUR_OF_DAY, 15);
int year = calendar.get(Calendar.YEAR);
int month = calendar.get(Calendar.MONTH)+1;
int date = calendar.get(Calendar.DAY_OF_MONTH);
int hour = calendar.get(Calendar.HOUR_OF_DAY);
int minute = calendar.get(Calendar.MINUTE);
int second = calendar.get(Calendar.SECOND);
*Math
System.out.println(Math.abs(-1));
System.out.println(Math.round(1.49));
System.out.println(Math.floor(-10.5));//比它小的第一个double类型
System.out.println(Math.ceil(-10.5));//比它大的第一个double类型
System.out.println(Math.max(10.5, 11));
System.out.println(Math.random());//[0,1)
//产生一个10到50的随机数
int number =(int)(Math.random()*41)+10;//[0,40]
System.out.println(number);
*Random(*****)
.nextInt(n);//[0,n)--随机数是根据随机种子来算出来的,所以每次要产生
不同的随机数,必须种子的值不同.
练习:String str = "aaabbsadsdvvsdasc";
求出现次数最多的那个字符,而且要输出次数
*异常
--在运行过程中出现的不正常事件,导致程序异常中断.
*异常类对象
--当一个程序在运行的过程中发生了异常事件,就会生成一个异常类对象,此异常类对象
内部封装了异常了一些信息,把异常对象交给java运行系统,java运行系统会把异常对象
交给相应的部门处理,处理不了就中断程序,能处理的话会改变程序的执行流接着往下走.
*异常的分类:
Throwable(可抛的)
--Error(错误的)--程序解决不了,往往是JVM的问题,我们一般不用去捕获.
--Exception(异常)--程序能解决的,比如数组越界,非法参数,数学异常等.
--RuntimeException(运行时异常)
--NullPointerException
--IndexOutOfBoundsException
--非RuntimeException异常
*异常按是否必须处理来分类
1.受查异常:
Exception 中除了RuntimeException以外的其它子类
特点: 在编译的时候必须要处理这个异常,不处理程序编译都通不过.不是程序能控制的,不如访问的文件被删除了
2.非受查异常
RuntimeException及其子类
特点:程序导致的问题,在编译的时候可以不处理.
*Java异常处理机制
1) try{
//监控的代码 --可能会抛出异常类对象
}catch(异常处理对象){
//处理异常代码
}finally{
//不管是否出现异常都会执行,只要用来释放资源
}
捕获处理异常--作用:防止程序以外中断
总结:
1.当try中程序正常执行的话catch是不会执行的,只有当抛出异常的时候才会到catch中匹配处理
2.可以设置多个catch,上面一般放具体的异常处理对象,下面放通用的异常处理对象
3.finally语句块不管有没有异常都会执行.就算碰到return语句,也会在返回之前
先执行finally语句块.
碰到 System.exit(0);//结束进程退出 ,finally语句块不会执行
4.try可以单独和catch使用,也可以单独和finally配合使用,也可以三者一起配合使用.
2. throw关键字(手动抛出异常)
--是用来手动抛出异常的.
--我们可以手动抛出已经存在的异常类对象,也可以抛出我们自定义的异常类对象
3. throws关键字(方法抛出异常)
--使用来修饰方法的,对外告知我这个方法可能会抛出异常,让调用者去捕获处理异常.
注意点:
1.当方法中抛出一个非受查异常,方法上的throws可以不用加,但养成好习惯一般都加上.
2.当方法中抛出一个受查异常,那么方法中要么自己try...catch处理
要么在方法上加上throws教由方法的调用者来处理
3.当调用抛出受查异常的方法的时候,调用者要么去捕获方法抛出的异常,要么继续往外抛.
养成一种好习惯,不管方法中抛出的是受查还是非受查异常,方法名后面都要加上throws.
*自定义异常:
--必须从已有的异常类来继承,是否是受查异常还是非受查异常跟父类走.
class LessThanEighteen extends Exception {
public LessThanEighteen(String msg) {
super(msg);
}
}
//1.在主方法中声明一个长度为5的整型数组,
要求通过一个方法把数组的第三个元素的值改为3,
最后在主方法中把下标为3的那个元素的值输出
-------------------------------------------------------------------------
泛型:
如何解决具有相同结构就数据类型不同的类的膨胀问题?
1.使用Object数据类型能解决类的膨胀问题
--会有两个问题
*必须把Object类型的数据进行强转成具体的数据类型
*编译通过的情况运行可能会出错. ClassCastException(类转换异常)
2.使用泛型就成彻底解决以上两个问题.
*泛型的原理
--就是"类型参数化"(就是在编译的时候检查与之对应设置的类型)
注意:不支持泛型数组.
*泛型类可以继承泛型类,也可以实现泛型接口
*限制泛型可用类型
--在定义泛型类别时,默认在实例化泛型类的实例可以使用任何类型,
但是如果想要限制使用泛型类型时,只能用某个特定类型或者是其子类型
才能实例化该类型时,可以在定义类型时,使用extends关键字指定这个类型必须是继承某个类,或者实现某个接口。
-- 当没有指定泛型继承的类型或接口时,默认使用extends Object,
所以默认情况下任何类型都可以做 为参数传入。
*类型通配声明
同一泛型类,如果实例化时给定的实际类型不同,则这些实例的类型是不兼容的,
不能相互赋值。
泛型类实例之间的不兼容性会带来使用的不便。
我们可以使用泛型通配符(?)声明泛型类的变量就
可以解决这个问题。
?:代表不确定的类型
*泛型类的类型参数可以给类中的哪些元素使用
1.属性的类型
2.方法的返回值数据类型
3.方法的形参数据类型
*泛型方法:--这个类型的参数只是针对方法本身,可以作为方法的形参,和方法的返回类型.
不针对其它任何元素
*一个类中有泛型方法,不代表这个类就是泛型类.
*一个泛型类中的静态方法是不能使用泛型类的类型作为方法的参数类型也不能作为方法的返回类型.
我们被静态方法设置为泛型方法就可以了.
------------------------------------------------------------------------------
集合框架(学一套容器和操作容器工具)
--一套容器的集合(包括接口,容器类,操作容器的工具类),每种容器都有各自底层的数据结构,不同的数据结构导致容器有
不同的性能,所以我们要根据实际的需求选择高效(合适)的容器.
集合框架层次结构
iterable(迭代器接口--提供统一标准用来遍历容器中的数据)
--Collection接口
--List接口--(特点:有序(存取顺序一致)可重复)
--ArrayList--(数据结构-数组)*****
--LinkedList--(数据结构-双向链表)
--set接口--(特点:无序(存取顺序不一致)不可重复)
--HashSet--(数据结构-哈希表)
--TreeSet--(数据结构-红黑树(二叉树的一种))
Map接口--(特点: 存放键值对的数据)
--HashMap--(数据结构-哈希表)*****
--TreeMap--(数据结构-红黑树(二叉树的一种))
Collections(类)
--它是一个工具类,里面提供了很多静态方法来操作相应的容器的,比如对容器中的
数据进行排序等.
*iterable接口
Iterator<T> iterator()
返回一个在一组 T 类型的元素上进行迭代的迭代器。
--Iterator迭代器接口
boolean hasNext()
如果仍有元素可以迭代,则返回 true。 (判断有没有元素)
E next()
返回迭代的下一个元素。(取出迭代到的元素)
void remove() --删除元素
从迭代器指向的 collection 中移除迭代器返回的最后一个元素(可选操作)。
*Collection接口,它是一个容器的根接口
*ArrayList
// list.clear();
// System.out.println(list.contains("chenhao"));
// System.out.println(list.get(3));
// System.out.println(list.size());
// System.out.println(list.get(20));
// System.out.println(list.indexOf("chenhao"));
// System.out.println(list.lastIndexOf("chenhao"));
// System.out.println(list.isEmpty());
// System.out.println(list.remove(1));
// list.set(3, "mary");
// System.out.println(list.toString());
/*Object[] arr = list.toArray();
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}*/
/*String[] arr = list.toArray(new String[10]);//转换给指定类型和指定长度的数组
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}*/
ArrayList 原理:
1.底层就是一个Object类型的数组,调用无参构造函数默认创建长度为10的Object类型
数组,添加元素的个数超过了数组的容量,会自动进行扩容,新Object数组的容量是
原来容量大小的1.5倍.并把原来数组中的数据拷贝到新数组中
2. ArrayList中的contains,indexOf,remove(Object),底层都是调用equals方法来判断的
所以我可以根据实际情况来重写equals方法来实现我们的效果.
ArrayList 特点:
查询效率很高,但删除,插入效率比较低(因为数组会有移位操作,所以效率比较低)
*LinkedList
它比ArrayList多了一些XXXFirst()和XXXLast();
这些方法是LinkedList特有的,因为它除了继承List接口以外,又实现了Deque<E>
接口
LinkedList 原理:
底层是双向链表数据结构,每个节点是LinkedList内部的一个静态内部类对象Node
它里面封装了三个元素,1.添加的内容,2.next--指向下一个Node对象 3.pre--指向
上一个Node对象.每添加一个size就加1.
LinkedList特点:
查询效率比较低,但插入,删除效率高(因为它只影响其前后两个Node对象,只是修改这两个对象的next和pre属性值)
*iterable(迭代器接口)
Iterator<T> iterator()
返回一个在一组 T 类型的元素上进行迭代的迭代器。
--Iterator迭代器接口
boolean hasNext()
如果仍有元素可以迭代,则返回 true。 (判断有没有元素)
E next()
返回迭代的下一个元素。(取出迭代到的元素)
void remove() --删除元素
从迭代器指向的 collection 中移除迭代器返回的最后一个元素(可选操作)。
迭代器的作用:
使用一种统一的遍历方式来遍历不同容器(实现Collection接口的所有容器)中的数据.
Iterator<String> it = data.iterator();
//判断容器中是都有下个元素
while(it.hasNext()){
String str = it.next();//取出迭代到的元素
System.out.println(str);
}*/
for(Iterator<String> it = data.iterator();it.hasNext();){
String str = it.next();//取出迭代到的元素
System.out.println(str);
}
*foreach循环(增强for循环)--能使用迭代器进行遍历的,也可以使用增强for寻来来遍历
for(String str : data){
System.out.println(str);
}
特点:语法简单,就是用来遍历数据,底层就是迭代器.
异常:ConcurrentModificationException--并发修改异常
当一个线程正在遍历容器中数据的时候,是不允许让另一个线程去修改容器中的数据
*Map接口的特点
--存放的是键值对数据,键和值都可以是任意对象,我们可以根据键来查询得到值.
--键值唯一,但值可以重复,每个键最多只能映射到一个值。
常用的API
void clear()
从此映射中移除所有映射关系(可选操作)。
boolean containsKey(Object key)
如果此映射包含指定键的映射关系,则返回 true。
boolean containsValue(Object value)
如果此映射将一个或多个键映射到指定值,则返回 true。
Set<Map.Entry<K,V>> entrySet() *****
返回此映射中包含的映射关系的 Set 视图。
boolean equals(Object o)
比较指定的对象与此映射是否相等。
V get(Object key) *****
返回指定键所映射的值;如果此映射不包含该键的映射关系,则返回 null。
int hashCode()
返回此映射的哈希码值。
boolean isEmpty()
如果此映射未包含键-值映射关系,则返回 true。
Set<K> keySet() *****
返回此映射中包含的键的 Set 视图。
V put(K key, V value) *****
将指定的值与此映射中的指定键关联(可选操作)。
void putAll(Map<? extends K,? extends V> m)
从指定映射中将所有映射关系复制到此映射中(可选操作)。
V remove(Object key) *****
如果存在一个键的映射关系,则将其从此映射中移除(可选操作)。
int size() *****
返回此映射中的键-值映射关系数。
Collection<V> values()
返回此映射中包含的值的 Collection 视图。
*HashMap(底层数据结构是哈希表)
--并允许使用 null 值和 null 键。
--不保证顺序
*HashMap和HashTable区别?--共同点:功能一样
1.HashMap允许键为null,而hashTable不可以
2.HashMap是线程不安全的,HashTable是线程安全的
*HashMap常用api
void clear()
从此映射中移除所有映射关系。
Object clone()
返回此 HashMap 实例的浅表副本:并不复制键和值本身。
boolean containsKey(Object key)
如果此映射包含对于指定键的映射关系,则返回 true。
boolean containsValue(Object value)
如果此映射将一个或多个键映射到指定值,则返回 true。
Set<Map.Entry<K,V>> entrySet()
返回此映射所包含的映射关系的 Set 视图。
V get(Object key)
返回指定键所映射的值;如果对于该键来说,此映射不包含任何映射关系,则返回 null。
boolean isEmpty()
如果此映射不包含键-值映射关系,则返回 true。
Set<K> keySet()
返回此映射中所包含的键的 Set 视图。
V put(K key, V value)
在此映射中关联指定值与指定键。
void putAll(Map<? extends K,? extends V> m)
将指定映射的所有映射关系复制到此映射中,这些映射关系将替换此映射目前针对指定映射中所有键的所有映射关系。
V remove(Object key)
从此映射中移除指定键的映射关系(如果存在)。
int size()
返回此映射中的键-值映射关系数。
Collection<V> values()
返回此映射所包含的值的 Collection 视图。
HashMap原理
底层数据结构:
1.数组加链表
2.先取key的hashcode值,再通过hash()散列函数来和hashcode运算,得到一个hash值
3.再通过hash&(数组的长度) == hash%数组的长度 得到 数组的下标
4.就算key的hashcode值不一样,但也有可能算出数组的下标一样,这叫"冲突"
5.假如冲突了,存在两种可能,
1.键的equals相同,那么就会把值覆盖掉.
2.键的equals不同,那么会将新添加的Node对象放在数组的位置,原来位置上
存在的那个元素会以链表的形式连接在新添加的Node对象的后面.
6.当添加进去的数据等于数组长度*填充因子(0.75)的时候,数组就会扩容,扩为原来的
2倍大小.
注意:我们put进去的键值对数据,都会封装成一个静态内部类对象(Node)
它里面封装了hash值,key,value,Node next.
Node 实现了 Map.Entry<K,V>接口
还有获取map容器中的键值对方式是获取容器中封装的所有Node对象,
调用其内部的getKey和getValue方法就能获取对应的键值对.
为什么要重写hashCode和equals方法?
重写hashcode值主要的目的是求出元素在数组中的下标
当两个对象equals相同,意味着 m1.hashCode()==m2.hashCode(),
反之当两个对象的hashcode值一样,但两个对象equals未必一样.
当两个对象的hashcode值一样并且equals值也一样,我们就可以认为是同一个对象.
*TreeMap--底层是红黑树(二叉树的一种)
特点:
1.该映射根据其键的自然顺序进行排序(数字,字符串),或者根据创建映射时提供的 Comparator
进行排序,具体取决于使用的构造方法。(自定义复杂对象)
TreeMap 原理
底层是一颗二叉树,每个添加进去的键值对元素都会封装成一个Entry对象,这个Entry
对象会作为树的一个个节点.要进行排序只有两种可能,一种是对象本身去实现comparable接口
去实现compareTo方法,另一种是TreeMap构造函数中传入Comparator接口,去实现compare方法
根据这两个方法的返回值来确定此Entry对象是放在二叉树的左边还是右边.
返回小于0的数,放左边,返回大于0的数放右边,返回0的数替换.
*HashSet
原理:
底层就是一个HashMap,HashSet调用add(值),无非就给给hashMap的键赋值了,也就是
说hashset只用hashMap的键,不用他的值.
*TreeSet
原理:
底层就是一个TreeMap,treeSet调用add(值),无非就给给TreeMap的键赋值了,也就是
说TreeSet只用TreeMap的键,不用他的值.
*Collections
Collections和Collection的区别?
Collection它是接口,是List和Set接口的父接口
Collections它是一个类,内部提供了很多静态方法用来操作集合框架的,方便操作.
*****Collections.sort();
作业:1.把ArrayList的购物车改成HashMap来做.
2.word上的作业
3.预习多线程
--------------
-----day18----
--------------
1.什么是程序?
--就是安装在磁盘上的一段指令,它是静态的.
2.什么是进程?
--运行中的程序,它是占用内存资源的.一个程序运行了就开启了一个进程,每个进行都
有独立的资源空间
3.什么是线程?
--线程就是一个程序的执行流.
--一个进程可以有多个线程,每个线程都可以完成一项任务.多个线程都是依附在进程上的
所以多个线程共享同一个进程的资源.这个就要多线程.
4.什么是多线程?
--一个进程中开辟多个线程来执行不同的任务,这就要多线程编程.
5.什么要进行多线程的编程?
--提高cpu的工作效率,cpu运行效率特别的高,所以往往会有空闲的时间,同步操作(单个线程的)的话
往往导致cpu资源的浪费,所以我们开启多个线程,让cpu提高工作效率.
6.什么并发,什么是并行操作?
并发:往往针对的是单核cpu,cpu会分配时间片来执行不同的操作
并行:多核cpu它才能做到让多件事情同时进行运行
7.java程序执行的过程?
启动JVM(开启了一个应用程序)--JVM会开启一个线程来执行main方法,此线程叫主线程.
8. Java虚拟机它是多线程还是单线程的?
它是多线程的,当垃圾比较多的时候,会开启另一个垃圾回收线程.
9. 在Java程序如何去创建线程(创建线程的方式)
1.继承Thread类
class MyThread extends Thread {
// 线程要做的事情
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
System.out.println("aa:"+i);
}
}
}
MyThread t = new MyThread();//创建一个线程类的对象
//t.run();//只是调用对象中的一个方法,不是启动线程
t.start();//开启一个线程
//使用匿名内部类创建的一个线程类对象并执行
new Thread(){
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
System.out.println("aa:"+i);
}
}
}.start();
2.实现Runnable接口
class MyRunnable implements Runnable {
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
System.out.println("aa:" + i);
}
}
}
Thread t = new Thread(new MyRunnable());
t.start();// 启动线程
//匿名内部类
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
System.out.println("aa:" + i);
}
}
}).start();
*Thread 和 Runnable创建线程对象的区别?
Thread:(线程类)
1.Thread类是真实的线程类,线程的相关方法都可以直接通过Thread对象调用.
2.Thread类也实现了Runnable接口
3.当一个类没有继承其它父类的时候又想让它变成线程类那么就使用这种方式,更方便.
Runnable:
--你可以认为就是对Thread类的补充
当一个类已经有了一个父类,又想变成线程类,那就去实现Runnable接口,扩展性比较好.
总结:
1.当调用Thread类无参数构造函数构造线程对象的时候,我们重写了Thread类中run方法
当调用start()方法启动线程的时候,线程会调用Thread对象中的run方法
2.当调用Thread类带有Runnable接口形参的构造函数构建对象的时候.我们调用
Thread对象中的start()方法来启动线程的时候,线程会调用Thread对象中的run方法
只是run方法中又去调用了实现Runnable接口实现类对象的run方法
-----------------------------------------------------------------------------
*线程的生命周期(*****)
1.新建
2.就绪(调用start()方法)
3.执行(获取了cpu的资源)
4.阻塞(释放cpu的资源)
5.死亡(异常中断,run方法运行完)
* 学习下Thread类的常用API
--线程的名字操作
*getName()--得到线程的名字
*setName(String name) --设置线程的名称
*Thread.currentThread()获取当前线程的引用
--守护线程
--当所有的用户线程运行完了,守护线程就结束了.
setDaemon(boolean on) --true 代表守护线程
将该线程标记为守护线程或用户线程。
--加入--插队(join())
让指定的线程插入进来先执行,执行完当前线程再接着往下执行.
--睡眠--sleep(毫秒数)
当睡眠的时候会释放cpu的资源处于阻塞状态,当睡眠时间到了再到就绪状态(抢cpu资源)
--腾出线程--yield()
暂停当前正在执行的线程对象,并执行其他线程。
--设置线程的优先级
setPriority(int newPriority)
更改线程的优先级。
最大10,最小1 中间5
-----------------------------------------------------------------------------------------------------
*线程数据共享
使用Runnable来实现线程,多个线程共享数据更方便一点,因为只产生一份
*线程的同步
--什么叫线程同步?
---当cpu正在执行一个线程的时候,我们不希望cpu去切换到其它的线程来运行,等这个
线程运行完指定的指令好以后再让cpu切换到其它线程来执行
--java实现同步的关键字 -synchronized
--实现线程同步的方式有两种
对象锁:--每一个对象都有唯一的一把锁.
1)同步块
synchronized(对象){
//同步代码
}
1.同步代码同时只能有一个线程能访问,当一个线程执行完了同步块,另一个线程
才有机会进入.
2.当一个线程进入同步块的时候,首先获取此对象的锁,再执行同步块中的代码
当其它线程也想进入同步块的时候,也必须获取此对象锁,要是对象锁被其它线程
持有了,那么此线程只能等待,等待对方释放对象锁.(拿到对象锁才能进入同步块)
2)同步方法
它会获取当前对象的锁,会让方法中的所有代码同步
注意:线程安全,就是加了synchronized,线程不安全就是没加synchronized
同步:就是一个线程做完一件事件,另一个线程再去做另一件事情,有先后顺序的
异步:多个线程同时并发的进行操作.
StringBuilder StringBuffer
------------------------------------------------------------------------------
JavaDay19
*死锁的概念(了解)
--彼此都拥有了对象向要的资源,又想要获取对方手中的资源.
注意:尽量避免同步块中嵌套同步块.
*懒汉式单例模式(多线程是不安全的)(了解)
*线程间的通信
线程间的通信
--要控制多个线程之间工作的一个规则.比如生产者消费者的问题
*处理线程间通信的方法
wait()--等待并释放当前所占用的对象锁,wait的线程必须通过其它线程调用
notify()或notifyAll()来唤醒
notify()--唤醒对象上任意一个等待的线程
notifyAll()--唤醒对象上所有等待的线程
以上三个方法是Object对象的方法
wait 和 sleep 的区别?
1.wait不会自己醒,wait的线程必须通过其它线程调用notify()或notifyAll()来唤醒
而sleep会自己醒来
2. wait和锁有关,所以必须出现在同步块或同步方法中,因为它会释放当前所占用对象的锁
而sleep()方法任何地方都可以用,和同步无关.
3.互斥锁
ReentrantLock --比 synchronized 关键字更强大
-lock()获取锁
-unlock()释放锁
Condition :--是一个监听器,监听获取对象锁的线程的状态(一个监听器就监听一个线程),
通过它可以唤醒指定的线程.
4.线程组(了解)
ThreadGroup
------------------------------------------------------------------------------
IO:输入输出流(Input/Output)--对输入输出设备进行读写操作.
IO框架
--File类:--磁盘文件系统(代表磁盘上的一个文件或文件夹)
--InputStream/OutputStream --字节输入输出流
--Reader/Writer --字符输入输出流
*按流的流向分类:
记住:以内存为参照物
--输入流: 从其它设备读到内存的,就要"输入流"
--输出流: 从内存中把数据写到其它设备的.叫"输出流"
*按流的操作单位划分:
--字节流:(8位) --读取以字节存储为单位的文件,比如二进制文件(图片,音乐,视频,压缩文件)
--字符流:(16位)--纯文本文件
*按流的功能分:
--节点流:直接和数据源进行打交道的流
--过滤流:是对节点流进行包装,使其功能变得更强大,不跟数据源打交道.
*字节流
InputStream --字节输入流的顶层抽象父类
abstract int read()
从输入流中读取数据的下一个字节。
int read(byte[] b)
从输入流中读取一定数量的字节,并将其存储在缓冲区数组 b 中。
int read(byte[] b, int off, int len)
将输入流中最多 len 个数据字节读入 byte 数组。
close()
关闭此输入流并释放与该流关联的所有系统资源。
OutputStream--字节输出流的顶层抽象父类
close()
关闭此输出流并释放与此流有关的所有系统资源。
void flush()
刷新此输出流并强制写出所有缓冲的输出字节。
void write(byte[] b)
将 b.length 个字节从指定的 byte 数组写入此输出流。
void write(byte[] b, int off, int len)
将指定 byte 数组中从偏移量 off 开始的 len 个字节写入此输出流。
abstract void write(int b)
将指定的字节写入此输出流。
*开发IO程序的过程
1.先导入具体的输入输出流(java.io包)
2.要捕获IOException(受查异常)
3.释放资源.
*FileInputStream和 FileOutputStream
FileInputStream
-read()-读取一个字节赋值给一个整型变量,读到文件末尾了就返回-1.
FileOutputStream
-write(int)--把一个字节写到输出流中
碰到两个问题:
1.我们读字节为什么要赋值给int类型,而不是byte类型?
我们读到-1的时候就会认为读到了文件末尾,所以假如中途读到11111111它的值就是-1,
程序就会结束了,避免这个问题我们在前面加上24个0.
当通过输出流调用write(int)方法的时候,它会先去除前面24个0,再写回去.
2.一个个读一个个写效率太低,对磁盘的操作太频繁,怎么解决这个问题
自定义缓冲区来解决,读一批,写一批提高整体的读写效率.
*ByteArrayInputStream和ByteArrayOutputStream(字节数组输入输出流)
ByteArrayInputStream通过构造函数把外部传过来的字节数组赋值给它内部的一个
字节数组.(内存读到内存)
*过滤流(包装流)--对节点流进行包装,使其在不修改节点流功能的情况下进行功能的扩展和加强.
FiterInputStream
--BufferedInputStream(自带缓冲区的字节输入流)
BufferedInputStream(InputStream in)
创建一个 BufferedInputStream 并保存其参数,即输入流 in,以便将来使用。
BufferedInputStream(InputStream in, int size)
创建具有指定缓冲区大小的 BufferedInputStream 并保存其参数,即输入流 in,以便将来使用。
*read()方法--对节点流的read方法进行了加强
不是一个个读了,是一开始先读一批数据放到缓冲区中,再从缓冲区中读取一个,直到读完缓冲区中的数据
再一次读一批放到缓冲区中.
--BufferedOutputStream(自带缓冲区的字节输出流)
write(int)--先一个个写到自带的缓冲区中,当缓冲区的数据放满了,再一次性写入到
输出流中,它多节点流的write方法进行了加强.
问题:
当缓冲区数据没有放满的时候,它是不会自动写出去的,所以会导致文件变小了.
如何解决此问题?
1.手动控制清空缓冲区写出
bos.flush()
2.释放资源
bos.close()--bos.close();//它会先清空缓冲区中的数据,再释放所占的操作系统的底层资源
总结:
1.自带缓冲区的BufferedInputStream和BufferedOutputStream性能没有我们自定义的缓冲区
效率高,因为它们内部用有2个缓冲区,多了从一个冲区拷贝到另一个缓冲区的步骤.
2.我们只要关闭(释放)包装流的资源就可以,因为它内部会去释放被包装流的资源.
*DataInputStream和DataOutputStream --- 扩充了对基本数据类型的读写操作(了解)
---------------------------------------------------------------------------
装饰模式(decorator)-->包装设计模式
--对原有被包装的类进行功能的扩展
---------------------------------------------------------------------------------
字符流:
Reader:--字符输入流的顶层抽象类
Writer:--字符输出流的顶层抽象类
*FileReader 和 FileWriter
FileReader:--读取的时候把16位字节转换成对应的字符,可能在编码表中查不到对应的字符
FileWriter:--把字符转换成字节写出
读和写用的是当前环境所对应的编码方式,gbk
强调:复制纯文本可以使用字符流也可以使用字节流,而复制二进制文件我们应该使用
的是字节流,字符流会导致文件异常.突出:不管如何复制文件都一件使用字节流
因为字节流拷贝中间没有任何转换过程,就是字节直接搬家.
什么情况下使用字符流?
1.读取文本文件直接显示(FileReader);
2.直接把字符串写到文本文件中(FileWriter);
*BufferedReader和BufferedWriter--带缓冲区的字符输入输出流(最好用的地方就是一行行读)
String readLine()
读取一个文本行。
//用默认编码读写字符
BufferedReader br = new BufferedReader(new FileReader("day20-IO.txt"));
BufferedWriter bw = new BufferedWriter(new FileWriter("chenhao.txt"));
String line;
while((line= br.readLine())!=null){
//System.out.println(line);
bw.write(line);
bw.newLine();
}
br.close();
bw.close();
//使用指定编码来读写字符
BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream("a.txt"),"utf-8"));
---------------------------------------------------------------------------
其它流学习:
*对象流:ObjectInputStream和ObjectOutputStream (对象的序列化和反序列化)
对象的序列化:就是把内存中对象持久化到磁盘文件中,writeObject(obj)
对象的反序列化:就是把磁盘中保存的对象还原到内存中.(具体类型)readObject()
强调:要被序列化的对象必须要实现Serializable接口
序列化版本号
serialVersionUID = -610281784014176455L;
作用:确保被序列化对象的版本号和反序列化出来的对象的版本号必须一致,不一致的话
反序列化就不会成功,认为是两个不同的对象.
*序列流(SequenceInputStream)
--可以同时把多个字节输入流进行合并成一个字节输入流
Vector<InputStream> v = new Vector<>();
v.add(new FileInputStream("zhangsan.txt"));
v.add(new FileInputStream("chenhao.txt"));
v.add(new FileInputStream("count.txt"));
Enumeration<InputStream> e = v.elements();
SequenceInputStream sis = new SequenceInputStream(e);
FileOutputStream fos = new FileOutputStream("dd.txt");
byte[] buff = new byte[1024];
int len;
while ((len = sis.read(buff)) != -1) {
fos.write(buff, 0, len);
}
sis.close();
fos.close();
*对比
ArrayList--->Vector
HashMap--->HashTable
Iterator--->Enumeration<E>
*打印流
PrintStream(字节打印流)
PrintStream 为其他输出流添加了功能,使它们能够方便地打印各种数据值表示
形式。它还提供其他两项功能。与其他输出流不同,PrintStream 永远不会抛出
IOException
println()方法:它输出的内容是String.valueOf()所转换的字符串内容
write(97)方法--是找编码表中的字符来输出
PrintWriter(字符打印流)
PrintWriter pw = new PrintWriter(new FileWriter("test2.txt"),true);
当调用print方法的时候会自动清空缓冲区输出.
*System.in ---键盘标准输入流,用来读取键盘输入的字节
System.out---标准的打印输出流,默认打印到控制台上
static InputStream in
“标准”输入流。
static PrintStream out
“标准”输出流。
*转换流(InputStreamReader和OutputStreamWriter)
InputStreamReader:读的时候把字节流转换成字符流
OutputStreamWriter:写出去的时候把字符流转换成字节流
作用:就是可以按照指定的编码方式来进行读写操作
FileReader--它底层读的字节只是读出来好后把它转换成了字符,它继承了InputStreamReader
FileWriter--写出去的时候先把字符转换成字节,再把字节写出去.它继承了OutputStreamWriter
------------------------------------------------------------------------------------
递归:
--就是方法内部再去调用方法本身.
--关键点要找出程序的出口
求:5! 5*4*3*2*1
f(5)=5*4!
f(4)=4*3!
f(3)=3*2!
f(2)=2*1!
return 1;===>出口
阶乘的函数内部就调用自身阶乘的函数,只是数字放生了变化
public static long fac(int number) {
if (number == 1) {
return 1;//回调
} else {
return number * fac(number - 1);
}
}
练习:打印输出文件夹中所有的.txt文件
*网络编程概述
--什么是计算机网络?
计算机网络,就是把分布在不同地理区域的计算机与专门的外部设备用
通信线路互连成一个规模大、功能强的网络系统,
从而使众多的计算机可以方便地互相传递信息,共享硬件、软件、
数据信息等资源。
--计算机网络体系结构
网络体系结构定义计算机设备和其他设备如何连接在一起以形成一个允许用户共享信息和资源的通信系统。
国际标准化组织ISO于l978年提出“开放系统互连参考模型”,即著名的OSI(Open System Interconnection)模型。
OSI模型保证了各类设备生产厂家的产品兼容性。
该模型把计算机网络分成物理层、数据链路层、网络层、传输层、
会话层、表示层、应用层等七层。
--OSI模型分层好处
1、不同厂商生产的设备都可以相互兼容。
2、设备可以专注于某一层的功能,如果交换机工作在第二层,路由器工作在第三层。
方便网络故障排错。
3、不用过多考虑物理接口等这些物理层的东西。
-TCP/IP通讯协议
TCP/IP是一组用于实现网络互连的通信协议。
Internet网络体系结构以TCP/IP为核心。
基于TCP/IP的参考模型将协议分成四个层次,
它们分别是:网络接口层、网络层、传输层、和应用层。
--网络编程的目的?
网络编程的目的就是指直接或间接地通过网络协议与其他计算机进行通讯。
网络编程中有两个主要的问题,一个是如何准确的定位网络上一台或多
台主机,另一个就是找到主机后如何可靠高效的进行数据传输。
*网络通讯的三要素
--什么是IP地址?
每个设备在网络中的唯一标识
每台网络终端在网络中都有一个独立的地址,我们在网络中传输数据就是
使用这个地址。
IP地址由32位二进制组成,占4个字节,常用十进制的格式表示,例如:192.168.0.5
对应的类-InetAddress
--查看本机ip和测试ip的命令
ipconfig:查看本地ip地址
ping:测试连接ip地址
--本机回路地址
127.0.0.1--localhost
--什么是端口号
端口号用来表示该计算机上的应用程序,代表此应用程序逻辑地址。
端口号使用一个16位的数字来表示,它的范围是0~65535,
1024以下的端口号保留给预定义的服务。例如:http使用80端口。
* mysql: 3306
* oracle: 1521
* web: 80
* tomcat: 8080
--什么是协议?
协议概念
为计算机网络中进行数据交换而建立的规则、标准或约定的集合。
常见的传输协议
TCP是Tranfer Control Protocol的简称,是一种面向连接的保证可靠传输的协议。
通过TCP协议传输,得到的是一个顺序的无差错的数据流。
三次握手: 客户端先向服务端发起请求, 服务端响应请求, 传输数据,
速度比较慢.
UDP是User Datagram Protocol的简称,是一种无连接的协议,
每个数据报都是一个独立的信息,包括完整的源地址或目的地址,
它在网络上以任何可能的路径传往目的地,因此能否到达目的地,
到达目的地的时间以及内容的正确性都是不能被保证的。 速度快
*Socket通信原理
* A:Socket套接字概述:
* 网络上具有唯一标识的IP地址和端口号组合在一起才能构成唯一能识别的标识符套接字。
* 通信的两端都有Socket。
* 网络通信其实就是Socket间的通信。
* 数据在两个Socket间通过IO流传输。
* Socket在应用程序中创建,通过一种绑定机制与驱动程序建立关系,告诉自己所对应的IP和port。
*UDP传输(了解)--不可靠的传输协议,速度比较快.它不分客户端和服务端,它只分为
接收端和发送端.
1.发送Send
* 创建DatagramSocket, 随机端口号
* 创建DatagramPacket, 指定数据, 长度, 地址, 端口
* 使用DatagramSocket发送DatagramPacket
* 关闭DatagramSocket
2.接收Receive
* 创建DatagramSocket, 指定端口号
* 创建DatagramPacket, 指定数组, 长度
* 使用DatagramSocket接收DatagramPacket
* 关闭DatagramSocket
* 从DatagramPacket中获取数据
3.接收方获取ip和端口号
* String ip = packet.getAddress().getHostAddress();
* int port = packet.getPort();
示例:
* 接收端Receive
*
DatagramSocket socket = new DatagramSocket(6666); //创建socket相当于创建码头
DatagramPacket packet = new DatagramPacket(new byte[1024], 1024); //创建packet相当于创建集装箱
while(true) {
socket.receive(packet); //接收货物
byte[] arr = packet.getData();
int len = packet.getLength();
String ip = packet.getAddress().getHostAddress();
System.out.println(ip + ":" + new String(arr,0,len));
}
* 发送端Send
DatagramSocket socket = new DatagramSocket(); //创建socket相当于创建码头
Scanner sc = new Scanner(System.in);
while(true) {
String str = sc.nextLine();
if("quit".equals(str))
break;
DatagramPacket packet = //创建packet相当于创建集装箱
new DatagramPacket(str.getBytes(), str.getBytes().length, InetAddress.getByName("127.0.0.1"), 6666);
socket.send(packet); //发货
}
socket.close();
###26.08_网络编程(UDP传输多线程)
* A发送和接收在一个窗口完成
public class Demo_MoreThread {
/**
* @param args
*/
public static void main(String[] args) {
new Receive().start();
new Send().start();
}
}
class Receive extends Thread {
public void run() {
try {
DatagramSocket socket = new DatagramSocket(6666); //创建socket相当于创建码头
DatagramPacket packet = new DatagramPacket(new byte[1024], 1024); //创建packet相当于创建集装箱
while(true) {
socket.receive(packet); //接收货物
byte[] arr = packet.getData();
int len = packet.getLength();
String ip = packet.getAddress().getHostAddress();
System.out.println(ip + ":" + new String(arr,0,len));
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
class Send extends Thread {
public void run() {
try {
DatagramSocket socket = new DatagramSocket(); //创建socket相当于创建码头
Scanner sc = new Scanner(System.in);
while(true) {
String str = sc.nextLine();
if("quit".equals(str))
break;
DatagramPacket packet = //创建packet相当于创建集装箱
new DatagramPacket(str.getBytes(), str.getBytes().length, InetAddress.getByName("127.0.0.1"), 6666);
socket.send(packet); //发货
}
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
网络编程(TCP协议)(掌握)
* 1.客户端
* 创建Socket连接服务端(指定ip地址,端口号)通过ip地址找对应的服务器
* 调用Socket的getInputStream()和getOutputStream()方法获取和服务端相连的IO流
* 输入流可以读取服务端输出流写出的数据
* 输出流可以写出数据到服务端的输入流
* 2.服务端
* 创建ServerSocket(需要指定端口号)
* 调用ServerSocket的accept()方法接收一个客户端请求,得到一个Socket
* 调用Socket的getInputStream()和getOutputStream()方法获取和客户端相连的IO流
* 输入流可以读取客户端输出流写出的数据
* 输出流可以写出数据到客户端的输入流