JavaSE笔记
Java的三大平台
JavaSE Java的标准平台:包含了Java的基础的功能,学习的语法,内置的API方法
JavaEE Java的企业级应用平台:JavaSE的基础上,扩展了一些API,用于B/S结构的企业应用的开发
JavaME Java的移动应用平台:JavaSE缩微版,给嵌入式系统或移动端系统使用过时
Java语言特点
1.跨平台 平台-操作系统
任何平台上编写的Java程序,可以在另外的平台上执行
JDK Java的开发环境
JRE Java的运行环境 - Java虚拟机
java文件 ->(编译) class文件 -> Java虚拟机中运行
2.面向对象: 以人类正常思维开发和设计软件,开发程序开发方法:支撑大型的关系复杂系统的构建
3.多线程:支持多任务场景
4.分布式:支持网络应用
5.简单:比C++简单 指针,回收(内存泄露)Java中没有指针, 系统自动回收
public class Demo1 {类块 ,类中的代码都要写在类块之中
public class Java中的关键字
主程序(main方法) :一个Java程序需要执行,必须有一个main方法
mian方法是程序的入口,需要程序执行的代码,一定要经过main方法
public static void main(String[] args) {方法块
System.out.println("中文");
Java程序之后的输出语句
System.out.println(123);
写完一行代码后,编译区出现红叉(报错) ,表示代码的写法已经
违反了Java的语法规则
System.out.println(123);
System.out.print("中文");不换行输出
}
变量:用于存储程序在计算过程中用到数据
public class Demo1 {
错误演示,不能存在相同的变量名
int a=5;
int a=4;
public static void main(String[] args) {
// TODO Auto-generated method stub
在Java中规定,变量需要先声明 (int a)
并且对变量进行初始化(对变量设置初始值),之后才可以正确使用
声明了一个变量 a , 变量a的值是5
int a=5;
int a=6; 错误演示,不能同时存在相同的变量名
使用输出语句打印一个变量时,实际的输出内容是变量所指代数据值
System.out.println(a);结果是5
变量所指代的数据值可以改变,也就是可以对变量进行重新赋值
a=6;
System.out.println(a); 结果是6
变量只需要声明一次即可在一个块内(方法块,类块)
不能同时出现多个相同名字的变量
变量的命名:
可以由字母,数字,”_”和“$”组成。
不能以数字开头,区分大小写,不能使用java的关键字和保留字
可以使用中文命名但不建议使用。
1.见名知意
int age=12;
int month=12;
2.驼峰命名法:如何变量名由多个英文单词组成
从第二个单词开始 每个单词首字母大写
int className=12;
int 年龄=5;//中文可以作为变量名,但是不建议使用
System.out.println(123456879*987654321);
int num1=123456879;
int num2=987654321;
System.out.println(num1*num2);
int class=5; Java中的关键字和保留字不能作为变量名
int class=2; 错误演示
}
}
数据类型
基本数据类型
整型
byte 占1个字节(一个字节占8位)-128-127
short 占2个字节 -2^15 - (2^15-1)
int 占4个字节 -2^31 - (2^31-1)
long 占8个字节 -2^63 - (2^63-1)
浮点型
float 占4个字节 单精度
double 占8个字节 双精度
字符型
char
布尔型
boolean
public static void main(String[] args) {
// TODO Auto-generated method stub
byte b=127; 声明byte类型变量 b 值为127
short s=255; 声明short类型变量 s 值为255
int i=1000; 声明int类型变量 i 值为1000
在Java程序中直接书写的整数值 (整数直接量),默认为int类型
如果需要声明数据为long类型,在数据的末尾加上 L/l
long l=154L;
double数据类型比float数据;类型更加精确
float f=5.2F; 单精度
double d=5.2; 双精度
值写在' ' 中,只能有一个字符
char c='中';
System.out.println(c+1);
boolean b1=true;
boolean b2=false;
}
基本数据类型转换
1.隐式转换(自动转换)
小的数据类型可以自动转换到大的数据类型
2.强制转换
大的数据类型转换到小的数据类型
目标数据类型 变量名=(目标数据类型) 需要转换的变量或者值;
int i=5;
byte b=(byte) i;
数据类型转换.png
在Java程序中直接书写的整数值 (整数直接量),默认为int类型
Java允许将整数直接量赋值给 byte ,short,char,不超出数据类型的取值范围即可
byte b=20;
System.out.println(b); 结果20
char c=25;
short s=22;
变量b是byte类型属于隐式转换,Java自动执行
int i=b;
System.out.println(i); 输出结果20
* 由于i1是int类型 ,将i1赋值给byte类型的变量 b1
* 属于 将大的数据类型转为小的的数据 Java中不允许直接操作
* 所有会出现编译报错,因为变量i1 不是整数直接量
byte b1=5;
int i1=12;
b1=i1;*/
byte ,short,char在参与运算时,会先转换为int
byte bt1=10;
bt1=bt1+5; int - > byte 大的数据类型转为小的数据类型, 编译报错
强制转换
int i=5;
byte b=(byte)i;
System.out.println(b);
double d=10;
System.out.println(d);
//进行强制转换,可能造成数据精度的缺失
int i1=(int)5.9;
System.out.println(i1);
System.out.println(1-0.1);
public static void main(String[] args) {
// TODO Auto-generated method stub
int a=5;
System.out.println(a);
a=10;
System.out.println(a);
* 声明int类型变量i1,无初始化
* 声明int类型变量i2,初始值10
* 所以,直接使用变量i1会编译报错
int i1,i2=10;
System.out.println(i1); 会出现编译报错
System.out.println(i2);
}
Java中的运算符
public static void main(String[] args) {
// TODO Auto-generated method stub
System.out.println(12*25);
Java中变量也可以进行运算
int n=9;
int m=2;
System.out.println(n+m);//11
System.out.println(n-m);//7
System.out.println(n*m);//18
如果参与除法运算的变量都是int类型,则其结果也是hint
System.out.println(n/m);//4
如果参与除法运算的两个变量类型不一致
其结果以大的数据类型为准
int a=3;
double b=9;
System.out.println(b/a);//3.0
求余结果的正负 取决于第一个数的正负
System.out.println(5%2);//1
System.out.println(5%-2);//1
System.out.println(-5%2);//-1
System.out.println(-5%-2);//-1
}
public static void main(String[] args) {
// TODO Auto-generated method stub
++ ,-- 自增,自减运算符
* 如果 int i=5; ++i 表示 在i初始值的基础上+1
* ++i 相当于 i=i+1;
int i=5;
System.out.println(i);//5
++i;
System.out.println(i);//6
int n=2;
int m=3;
* 前置 ++ :
* int n=2; 如果使用++n (在输出语句中,在运算表达式中都表示使用)
* 这种情况下,++n的值为 初始值+1
System.out.println((++n)+(++m));//7
System.out.println(n);//3
System.out.println(m);//4
* 后置 ++ :
* 如果使用n++(在输出语句中,在运算表达式中都表示使用)
* 这种情况下,n++的值为 n的原始值 ,但是之后再使用n时,n的值是+1后的结果
System.out.println(n++);//2
System.out.println(n+1);//4
}
+=的特殊情况
public static void main(String[] args) {
// TODO Auto-generated method stub
int n=2;
n+=5; 表示 n=n+5;
System.out.println(n);
int m=10;
m%=4;m=m%4;
System.out.println(m);
byte b=2; b=b+1; 会编译报错
b+=1;该情况不会出现编译报错 ,Java自动执行了强制转换
}
比较运算符
public static void main(String[] args) {
// TODO Auto-generated method stub
比较运算符:比较两个数值或者变量之间的大小关系
int n=5;
int m=4;
int k=4;
逻辑表达式:比较两个或者多个变量之间的关系算式
逻辑表达式的结果为boolean - true(成立) , false(不成立)
System.out.println(n>m);//true
System.out.println(n<m);//fasle
System.out.println(n>=m);//true
System.out.println(n<=m);//false
System.out.println(n!=m);//true
System.out.println(n==m);//fasle
System.out.println(m==k);//true
}
逻辑运算符
public static void main(String[] args) {
// TODO Auto-generated method stub
逻辑运算符
逻辑与 &&
&&左右两边的表达式同时成立,整个表达式才成立
int a=5;
int b=10;
System.out.println(a<b&&b>12);//false
System.out.println(a<b&&b>5);//true
逻辑或 ||
||左右两边的表达式,只要有一端成立,整个表达式就成立
如果||两端的表达式都不成立。则整个表达式不成立
int a1=12;
int b1=20;
System.out.println(a1<=20||b1>100);//true
System.out.println(a1>=20||b1>100);//false
逻辑非 !
对逻辑表达式的结果取相反的结果
System.out.println(a1>b1);//fasle
System.out.println(!(a1>b1));//true
System.out.println(!true);//fasle
}
短路问题
* 对于&&来说,如果左边的条件表达式结果明确为fasle
* java编译器则不会再执行&&右边的条件表达式代码
* 对于||来说,如果左边的条件表达式结果明确为true
* java编译器则不会再执行||右边的条件表达式代码
int a=10;
int b=15;
System.out.println(++a<10&&++b>10);//false
System.out.println(a);//11
System.out.println(b);结果15 由于&&短路问题 ++b没有被执行 ,结果仍是15
字符串拼接
public static void main(String[] args) {
// TODO Auto-generated method stub
引用数据类型
String 字符串 String str="hello";
String str="hello1";
System.out.println(str);
* 当+(加号)的左边或者右边出现字符串时(String ,"")
* 则加号不再是加法运算的功能,而是字符串拼接功能
int n=10;
int m=5;
System.out.println("n="+n);此时加号将双引号的内容与变量n的值拼接在一起
System.out.println("n="+n+", m="+m);
int age=12;
int month=12;
System.out.println("年龄:"+age);
System.out.println("月份:"+month);
}
三元运算符
public static void main(String[] args) {
// TODO Auto-generated method stub
三元运算符
* 公式: 条件表方式?表达式1:表达式2;
* 如果条件表方式的值为true 则返回表达式1的结果
* 如果条件表方式的值为false 则返回表达式2的结果
int a=1;
int b=2;
System.out.println(5==2?a+b:a-b);
}
流程控制语句
* 语法:
* if(条件表达式){//if语句块
* 语句块内容的代码(不固定)
* }
*
* 如果条件表达式的结果为true, 则执行语句块内的代码
* 如果条件表达式的结果为false,则不执行语句块内的代码
* if(条件表达式){
*
* }else{
*
* }
* 如果if中的条件表达式结果为true, 则执行if语句块的代码
* 如果if中的条件表达式结果为false,则执行else语句块的代码
int m=1;
if(m>=2){
System.out.println("1>=2");
}else{
System.out.println("1<2");
}
* if-else if
* 语法结构不固定,可以写多个else if条件
* 其中每一个if语句块都是独立,只会执行其中一个
* 如果存在多个条件成立的if语句块
* 则执行第一个成立的if语句块
int m=5;
if(m>8){
System.out.println("a");
}else if(m>4){
System.out.println("b");
}else if(m>1){
System.out.println("c");
}
* switch(num){括号中的内容是整型数值
* case 1:
* 执行的代码;
*
* case 2:
* ...
* break; 保证每个case语句独立执行
*
* default:如果所有case都不符合条件,则执行default
int num=5;
switch(num){
case 2:
System.out.println("b");
break;
case 3:
System.out.println("c");
break;
case 1:
System.out.println("a");
break;
case 4:
System.out.println("d");
break;
default:
System.out.println(num);
}
switch中可以使用的数据类型
switch()中可以使用那些数据类型
int ,byte ,short,char可以
long 不可以
JDK1.7开始 switch中可以使用字符串作为参数(指的的switch括号中的内容)
* while(条件表达式){
* 执行的代码
* }
*
* 首先判断条件表达式结果,如果为true
* 则执行while语句块中的代码,代码执行之后
* 再一次判断条件表达式的结果是否为true
* 如果为true ,则继续执行代码
* 直到条件为false的时候,结束循环
int m=10;
while(m>1){
if(m%2==0){
System.out.println("m="+m);
}
m--;
}
do-while:先执行一次代码,再进行判断
int n=1;
do{
System.out.println("n="+n);
n++;
}while(n<=10);
* for(循环变量的初始化;循环条件;循环变量的改变){
* 执行的代码
* }
* 执行顺序:
* 1.循环变量的初始化
* 2.循环条件
* 3.执行的代码
* 4.循环变量的改变
* 5.循环条件
* 如果条件为true,重复以上操作
* 如果条件为false,结束for循环
for(int i=1;i<=10;i++){
System.out.println(i);
}
循环嵌套:应用单层for循环的执行过程,依次执行
for(int i=1;i<=5;i++){
for(int j=1;j<=5;j++){
System.out.println("i="+i+" j="+j);
}
}
* 中断循环
* break:如果在循环结构中执行了break,
* 则整个循环停止,继续执行循环外的代码
*
* continue:如果在循环结构中执行了continue,
* 会跳过当前循环,执行下一次循环
for(int i=1;i<=10;i++){
if(i==5){
break;
}
System.out.println("当前i="+i);
}
System.out.println("华丽的分割线--------------");
for(int j=1;j<=10;j++){
if(j==5){
continue;如果执行力continue,则它之后代码不会执行
直接进行下一次循环
}
System.out.println("当前j="+j);
}
变量作用域问题
变量只在声明时它所在的块内有效
int n=2;
if(n>1){
int m=5;
System.out.println(m);
}
System.out.println(m); 会出现编译报错,无法使用变量m
数组
数组:用于存储一组相同数据类型的容器
* 数组中的数据叫做元素,每一个元素都有一个对应的下标
* 第一个元素,下标为0,依次递增
* 数组中元素的个数,表示数组的长度
创建数组的方式
创建数组 声明一个int类型的数组arr 长度是5
int[] arr=new int[5];
声明一个int类型的数组arr1 ,数组中的元素分别是1,2,3
int[] arr1=new int[]{1,2,3};
int [] arr2={1,2,3};
要求如果声明了具体的长度就不能声明具体的元素
如果声明了具体的元素就不能声明具体的长度
int[] arr2=new int[5]{1,2,3,4,5}; 编译报错
会出现编译报错,数组只能存放相同数据类型的元素
int[] arr3=new int[]{2.1,5,3};
不同数据类型数组的默认值
int[],byte[],short[],long[]中元素默认值是0
char[] 元素默认值是空字符
double[],float[]元素默认值0.0
boolean []元素默认值false
操作数组中的元素
int[] arr=new int[]{4,5,7,3,1};
int num=arr[2];获取元素值
System.out.println(num);
arr[0]=9;对元素赋值
System.out.println(arr[0]);
数组的长度属性 length, arr.length
System.out.println(arr.length);
arr.length=20; 只读属性,不能重新赋值,会编译报错
遍历数组:将数组的每一个元素获取出来
int[] arr=new int[]{5,4,6,1,2};
for(int i=0;i<arr.length;i++){
System.out.println(arr[i]);
}
在编码时,如果使用了数组中不存在的下标,并不会出现编译报错
但是在程序执行时,会出现数组下标越界的异常
System.out.println(arr[5]);
java.lang.ArrayIndexOutOfBoundsException
二维数组:数组中每一个元素还是一个数组
int[][] arr1=new int[2][3];
int[][] arr=new int[][]{{4,5},{9,2,3}};
System.out.println(arr.length);外层数组的长度
System.out.println(arr[0].length);内层小数组长度
System.out.println(arr[1].length);内层小数组长度
该声明方式不会编译报错,但运行程序则出现空指针异常
int[][] arr2=new int[2][];
java.lang.NullPointerException 空指针异常
由于内层数组并不存在,arr2[0],arr2[1] 的值为null
使用null掉用数组的属性,会报出空指针异常
操作二维数组元素
int[][] arr=new int[][]{{4,5},{9,2,3}};
System.out.println(arr[0][0]);
System.out.println(arr[1][2]);
...
遍历二维数组
for(int i=0;i<arr2.length;i++){
for(int j=0;j<arr2[i].length;j++){
System.out.println(arr2[i][j]);
}
}
冒泡排序
每一轮相邻的两个数之间进行两两比较,如果前一个数比后一个数大
则交换位置,一轮之后则找出最大值。之后其余的数据进行新一轮的
两两比较找出最大值,直到没有相邻的两个数再需要比较。
* 冒泡排序: 9,3,8,2,7,1
* 第一轮 :3,8,2,7,1,9 5次
* 第二轮: 3,2,7,1,8 4次
* 第三轮: 2,3,1,7 3次
* 第四轮: 2,1,3 2次
* 第五轮: 1,2 1次
for(int i=1;i<arr.length;i++){表示需要比较多少轮
for(int j=0;j<arr.length-i;j++){表示每轮需要比较多少次
if(arr[j]>arr[j+1]){如果前一个元素大于后一个 互换位置
int temp=arr[j];
arr[j]=arr[j+1];
arr[j+1]=temp;
}
}
}
快速排序
int []arr=new int[]{9,3,8,2,7,1};
快速排序方法 只能按照升序排列
Arrays.sort(arr);
1和5表示下标范围,可以修改。
Arrays.sort(arr,1,5);
数组的复制
int[] arrOld=new int[]{5,9,4,5,4,8};
int[] arrNew=new int[6];//0 0 9 4 5 0
* 数组的复制
* 第一个参数:源数组(被复制的数组)
* 第二个参数:从源数组的第几个下标开始复制元素
* 第三个参数:目标数组(接收复制元素的新数组)
* 第四个参数:从目标数组的第几个下标开始放置元素
* 第五个参数:从源数组中复制几个元素
System.arraycopy(arrOld, 1, arrNew, 2, 3);
int[] arrOld=new int[]{5,9,4,5,4,8};
* 该方法会创建一个新数组
* 第一个参数:源数组
* 第二个参数:新数组的长度
int[] arrNew=Arrays.copyOf(arrOld,6);
内存空间
内存图.png方法的定义
* 方法:用于解决一段具有特定功能的代码
*
* 方法的定义:
* [修饰词] 返回值类型 方法名([参数类型 参数名]){ 方法体 }
*
* 修饰词 :public ,static等关键字组成 表示该方法的使用(访问)的权限
*
* 返回值类型:
* 如果方法中的代码经过计算后会得到一个结果
* 那么该结果的数据类型 就是方法的返回值类型
* 需要使用关键字 return 返回该结果
*
* 如何方法中的代码,没有结果值(输出语句不算结果值)
* 则方法的返回值类型是 void 关键字
*
* 方法是先声明,再实现内容功能代码
* 所以,方法内部有没有结果值,应该取决于是否定义返回值类型
*
* 方法名:就是方法的被执行(被调用)的标识
*
* 参数:参数表示方法中需要的某些不确定的数据
* 数据类型 ,变量名
* 参数的个数不固定,可以没有,也可以存在多个
* 根据方法的具体需求来定
*
* 方法体:表示该方法核心功能的代码
方法和方法之间是同级关系,不能在方法中定义方法
如果方法想要被执行,则需要在main方法中调用该方法
程序中方法之间可以相互调用,但不建议调用main方法
无返回值无参数类型
public static void sayHi(){
System.out.println(1);
}
调用无返回值无参数类型方法
public static void main(String[] args) {
sayHi();
}
定义具有返回值类型的方法
如果定义的方法具有明确的返回值类型
则方法的内部必须返回一个对应的结果
public static int test1(){
return 2;
}
public static double test2(){
return 1.0;
}
public static boolean test3(){
return false;
}
调用方式
public static void main(String[] args) {
调用有返回值的方法
存在返回值的方法,可以当做一个值使用
System.out.println(test1()); - -2
short a=(short)test1();
System.out.println(a); - -2
}
如果在方法中出现return ,表示方法的结束,不再向下执行代码。
定义具有参数的方法
参数根据方法的功能来设定
求数值5 加上一个未知数的和
public static void test4(int a){
System.out.println(5+a);
}
public static void test5(int a,int b){
System.out.println(a+b);
}
public static int test6(int n){
如果在方法中出现return
表示方法的结束,不再向下执行代码
return n+5;
}
调用方式
调用有参数的方法,需要传入与参数类型对应的数据
public static void main(String[] args) {
test4(5);
test5(10,2);
System.out.println(test6(55));
}
方法调用的过程:实际就是将实参传递给形参的过程
public static void main(String[] args) {
// TODO Auto-generated method stub
sum(1,2);实参
int a=1;
int b=2;
sum(a,b);实参
}
public static int sum(int a,int b){形参
return a+b;
}
方法定义练习
求1—100之间的整数和
public static int test1(){
int sum=0;
for(int i=1;i<=100;i++){
sum+=i;
}
return sum;
}
求1—100之间能被3整除的数有多少个
public static int test2(){
int count=0;
for(int i=1;i<=100;i++){
if(i%3==0){
count++;
}
}
return count;
}
求两个整数区间内的所有整数和
public static int test3(int a,int b){
int sum=0;
if(a==b){
sum=a;
}else if(a<b){
for(int i=a;i<=b;i++){
sum+=i;
}
}else{
for(int i=b;i<=a;i++){
sum+=i;
}
}
return sum;
}
随机输入不相同的三个整数 比较出其中的最大值
public static int test4(int a,int b,int c){
int max=0;
if(a>b&&b>c){
max=a;
}else if(a>c&&c>b){
max=a;
}else if(b>a&&a>c){
max=b;
}else if(b>c&&c>a){
max=b;
}else{
max=c;
}
return max;
}
求两个整数之间存在多少个奇数
public static int test5(int a,int b){
int count=0;
if(a>b){
for(int i=b;i<=a;i++){
if(i%2!=0){
count++;
}
}
}else{
for(int i=a;i<=b;i++){
if(i%2!=0){
count++;
}
}
}
return count;
}
随机输入一个年份 判断是否为闰年
public static void test6(int year){
if(year%4==0&&year%100!=0||year%400==0){
System.out.println("闰年");
}else{
System.out.println("不是闰年");
}
}
有5位学员参加了Java知识竞赛的决赛,输出决赛的平均成绩
public static double test7(int[] scores){
double avg=0;//平均值
double sum=0;//总和
for(int i=0;i<scores.length;i++){
sum+=scores[i];
}
avg=sum/scores.length;
return avg;
}
求一个整数的阶乘
public static int test8(int n){
int num=1;
for(int i=n;i>0;i--){
num*=i;
}
return num;
}
计算数列 升序后的排列结果
public static void test9(int[] arr){
Arrays.sort(arr);
for(int i=0;i<arr.length;i++){
System.out.println(arr[i]);
}
}
方法的重载
在同一个类中,允许存在多个相同名字的方法
重载的规则:
方法名相同
参数不同:个数不同或者类型不同
返回值类型可以相同,也可以不同
面向对象
类:Java中一种特殊的数据类型 - -引用数据类型
类:是一组具有相同特征和行为的事物的抽象
对象:类的一个具体的实例
类可以衍生出不同的对象,每个对象都有着类中共同的特征和行为
类的定义
public class Person {
特征在类中叫做属性 (与之前学的变量相同)
String name;//名字
int age;//年龄
double height;//身高
double weight;//体重
构造器(构造方法)
* 编写一个类,一定要含有一个构造器
* 如果编写的类中,没有任何形式的构造器
* Java编译器则会提供一个默认构造器
*
* 如果类中已经存在了一种形式的构造器
* 则编译器不再提供默认构造器
public Person(){无参构造器/默认默认构造器
}
构造器的重载
提供有参构造器 在创建对象时,可以直接对属性赋值
对属性进行初始化操作
public Person(int age1){有参构造器
age=age1;
}
行为在类中叫做方法
public void eat(){
System.out.println("吃饭...");
}
public void sleep(){
System.out.println("睡觉...");
}
}
调用类中的属性和方法
创建类的对象 类名 对象名(与变量名同理) = new 构造器;
Person p1=new Person();
通过对象.属性 和 对象.方法 进行调用.
类中的属性具有默认值
System.out.println(p1.name);
p1.eat();
不同的对象之间不会相互影响
Person p1=new Person();
System.out.println("p1:"+p1.age);
System.out.println("p1:"+p1.name);
Person p2=new Person();
System.out.println("p2:"+p2.age);
System.out.println("p2:"+p2.name);
创建对象时,构造器的形式必须是在类中已经存在
否则无法使用,会出现编译报错
this:表示当前类的对象
* 使用有参构造器对属性赋值
* 如果参数名与属性名相同时
* 提供this进行区分
*
* this:表示当前类的对象
public Student(int age){
this();表示当前类的无参构造器
this.age=age;
System.out.println("有参构造器");
}
内存空间
栈:存取速度比堆快,效率高。
栈内保存基本数据类型的局部变量和对象的引用
堆:保存对空间要求大的内容,如对象的属性,数组的元素
内层图.png
Java中的变量类型
局部变量:声明在方法中的变量或者方法中的参数
实例变量:类中的属性,又叫全局变量。
静态变量:类中static修饰的属性,又叫类变量
匿名对象 :如果对一个对象只需要进行一次方法的调用
new Person();
垃圾回收机制
Java中的垃圾回收机制是自动的,垃圾回收机制实际上是JVM内部一个优先级比较低的后台线程,垃圾回收机制仅作用于堆,与栈无关
产生垃圾的情况
- 对象的引用被赋值为null
- 一次性使用匿名对象
- 超出生命周期的对象
变量的作用域
类变量:在类被加载时创建,只要类存在,静态变量就存在。
实例变量:在类的整个生命周期都有效
局部变量:只在方法的调用过程中有效,方法调用结束后失效
面向对象- - 继承 (子类 extends 父类)
Java中一个类可以继承另一个类,被继承的类 称为父类,另外一个称为子类
子类继承父类之后,可以使用父类的属性和方法
但是父类不能使用子类的属性和方法
继承的优点
有利于程序的扩展, 增强了代码的复用
继承的特性
单一性继承 ,一个子类只能有一个父类,但一个父类可以有多个子类
子类实例化过程
- 子类实例化时,先实例化其父类,再实例化子类
- 子类实例化会先实例化其父类,父类构造器调用完毕,才执行子类构造器
- 子类继承父类,在子类的构造方法的第一句一定会调用父类的构造方法
- 如果父类没有明确的构造方法,父类使用的是默认构造器。
- 在子类构造方法中会默认调用父类的默认构造器, super()可以不写
- 子类在自己的构造器中使用super关键字调用父类的构造器 super(参数1)
- 如果子类调用了父类的无参构造器 而父类中没有无参构造器则会编译报错
super关键字
作用:调用父类构造器 super( );
super( )括号中参数列表的形式,决定调用父类那个形式的构造器
只能出现在子类构造器中,且必须在第一行
面向对象 - - 封装
封装: 对类中的属性和方法规定访问权限,保护类中数据的安全
将类中的信息(属性和方法)隐藏起来,不允许外部程序直接访问,
而是通过该类 提供的方法进行访问
访问权限:用来控制类的成员和类的使用范围
- private:私有地
- default:默认地
- protected:受保护的
- public:公共的
权限.gif访问权限可以修饰属性和方法 ,其中public 和 默认 可以修饰类
面向对象 - - 多态
多态的体现:子类和父类之间,不同的子类之间对于同一行为,有不同的实现方式
方法的重写
对从父类中继承的方法进行重新改造
方法重写的规则
- 方法名相同
- 参数列表相同(数量,类型,顺序相同)
- 返回值类型相同
- 子类方法的访问权限不小于父类方法的访问权限
重写方法被调用时,看对象类型。子类对象的重写方法被调用时,引用类型无论是父类还是子类,运行的都是子类重写后的版本。
重写和重载的区别
重写:遵循“运行期”的绑定,根据对象类型进行调用
重载:遵循“编译器”的绑定,根据参数列表不同进行调用
多态的体现:引用数据类型的转换
上溯造型(向上造型):父类的引用指向子类的对象
优点:有利于程序的维护和扩展。
static关键字
static可以修饰属性,方法和代码块,但是不能修饰局部变量
静态变量- static修饰的变量
该变量也称为类变量,被该类的所有对象共享,在类被加载时创建
只要类存在,静态变量就存在
访问方式:类名.属性名 或者 对象.属性名
静态方法 - -static修饰的方法
不需要实例化对象,可以直接访问
访问方式:类名.方法名 或 对象.方法名
静态方法的注意事项
- 静态方法中只能直接访问静态属性
- 静态方法中不能使用this,super
- 静态方法不能被重写,不能修饰构造器
静态块 - - static修饰的代码块
当前类被加载时,静态代码块被执行,且只被执行一次
通常用来对属性进行初始化,加载静态资源
单例模式:保证一个仅有一个实例
单例编写模式
- 私有构造器
- 该类对象作为私有静态属性
- 公共的静态方法
final关键字
final修饰的类不能被继承
final修饰的方法不能被重写
final修饰的变量不能被重新赋值
如果声明属性时使用fianl修饰,属性不再有默认值
常量 :所有对象共享,不需要经常修改的数据 - static final
Java的异常处理机制
Java程序在编译或运行期间,发生的不可预期的意外,造成程序不能继续执行的情况
Java中的异常是被管理起来的,当程序发生异常,会创建出一个异常的对象
Throwable 所有异常的父类
Error 错误:天灾 人力不可抗拒 计算机硬件的问题,比如:硬盘坏了,内存掉电
Exception 异常: 人祸 主要是因为程序开发者不严谨造成的问题,可以通过代码来修补和解决
检查性异常
非检查异常
异常捕获机制
try{
可能发生异常的代码
}catch(Exception e){
发生异常则执行此处代码
}finally{
无论如何都会执行
}
throws 表示某个方法向上抛出异常
throw 人工制造异常
Object类:Java中所有类的父类
创建一个自定义类, 类的对象会调用出一些
类中不存在的方法,这些方法的都是继承于Object类
Object类中的方法
equals(Object obj) -boolean
比较两个对象是否为同一个对象(实际比较对象的地址是否相同)
Person p=new Person(12,"jack");
Person p1=new Person(12,"jack");
System.out.println(p.equals(p1)); - -false
== 和equals 的区别
== :比较基本数据类型的变量时,比较的是变量的值是否相同
比较引用数据类型时,比较的两个对象的地址是否相同
equals():Obejct类中的方法,默认比较的是两个对象的地址是否相同
一般Object的子类会重写该方法,从而比较的的是内容是否相同
在Person类中重写equals( )
重写equals() 从而比较两个对象之间的内容是否相同
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Person other = (Person) obj;
if (age != other.age)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
测试类
Person p=new Person(12,"jack");
Person p1=new Person(12,"jack");
System.out.println(p.equals(p1)); - -true
hashCode() 返回对象的哈希值(int)
如果两个对象的equals()比较为true,则两个对象的hashCode值相同
如果两个对象的equals()比较为false 那么两个对象的hashCode值可能相同,也可能不同
toString( ) 返回对象的字符串表现形式
直接使用输出语句打印一个对象 会得到该对象地址的字符串表现显示
子类重写Object中的方法,直接输出的对象 会得到对象的属性信息
@Override
public String toString() {
return "Person [age=" + age + ", name=" + name + "]";
}
包装类
//包装类和基本数据类型之间可以直接转换
Integer m=20;
Integer k=n;
System.out.println(Integer.MAX_VALUE);
System.out.println(Integer.MIN_VALUE);
//Integer.parseInt(String s) -int
//将字符串转为int 方法中的字符串必须是纯数字形式的
//否则会出现转换异常 java.lang.NumberFormatException
String number="12";
System.out.println(number+1);//121
int a=Integer.parseInt(number);
System.out.println(a+1);//13
//Integer.valueOf(String s) - Integer
String num1="12";
Integer numi=Integer.valueOf(num1);
System.out.println(numi);
//Integer.toString(int i) -String
String str=Integer.toString(5);
System.out.println(str+1);
Math类
//Math类中的属性和方法
System.out.println(Math.PI);
//绝对值方法 Math.abs(int i) -int
System.out.println(Math.abs(-20));
//求立方根方法 Math.cbrt(double d) - double
System.out.println(Math.cbrt(8));
//向上取整 ,返回大于或等于参数的最小整数 Math.ceil(double d) -double
System.out.println(Math.ceil(-0.0));
//向下取整 ,返回小于或等于参数的最大整数 Math.floor(double d) -double
System.out.println(Math.floor(0.9));
//返回两个数的最大值 Math.max(int a, int b) -int
System.out.println(Math.max(5, 8));
//返回两个数的最小值 Math.min(int a, int b) -int
System.out.println(Math.min(10, 8));
//返回[0.0,1.0)之间的随机小数 Math.random() - double
System.out.println(Math.random());
//四舍五入 Math.round(double d) - long
System.out.println(Math.round(5.0));
//求平方根方法 Math.sqrt(double d) -double
System.out.println(Math.sqrt(25));
System.out.println(Math.random()*10+5);
Random ran=new Random();
int d=ran.nextInt(1000);//[0,1000)
System.out.println(d);
Date类
- 创建Date对象
Date date =new Date( ); 格林威治时间的当前系统时间
-
getTime( ) - long 获取当前时间距1970年的毫秒值
-
setTime( ) - Date
SimpleDateFormat 日期格式转换
SimpleDateFormat sdf=new SimpleDateFormat("yyyy年MM月dd日 hh:mm:ss:SS");
Date date=new Date();
System.out.println(date);//Mon Aug 20 16:42:32 CST 2018
--format(Date d) -String 将日期类型的数据转换成字符串形式
String time1= sdf.format(date);
System.out.println(time1);
日历类
Calendar cal=Calendar.getInstance();
System.out.println(Calendar.YEAR);
System.out.println(cal.get(Calendar.YEAR));
System.out.println(cal.get(Calendar.MONTH)+1);//月份比实际值少1
System.out.println(cal.get(Calendar.DATE));
System.out.println(cal.get(Calendar.HOUR));
System.out.println(cal.get(Calendar.MINUTE));
System.out.println(cal.get(Calendar.SECOND));
String类
字符串的定义
String str="abc";
String str1="";//空字符串
System.out.println(str1);
String str2=new String();//空字符串
System.out.println(str2);
String str3=new String("hello");
System.out.println(str3);
字符串的方法
字符串也存在下标 ,第一个字符下标为0
String str="Tinking in Java";
charAt(int index) -char 根据参数下标返回对应的字符
System.out.println(str.charAt(7));
length() -int 获取字符串长度
System.out.println(str.length());
concat(String s) -String 将参数字符串连接到原字符串的末尾
得到一个新的字符串 对原字符串的内容没有影响
System.out.println(str.concat(" hello"));
* Tinking in Java
* 子串:in,ava
endsWith(String s) -boolean 判断当前字符串是否以参数(子串)为后缀
System.out.println(str.endsWith("Java"));
//equals(Object obj) - boolean 判断字符串与参数内容是否相同
System.out.println(str.equals("abc"));
indexOf(String s) -int 返回参数字符串在原字符串中第一次出现的位置
位置以参数字符串中第一个字符为准
如果不存在参数字符串 则返回 -1
System.out.println(str.indexOf("in"));
System.out.println(str.indexOf("abc"));
indexOf(String s, int i) - int
从指定下标开始查找,参数字符串在原字符串中第一次出现的位置
System.out.println(str.indexOf("in", 2));
lastIndexOf(String s) -int 返回参数字符串在原字符串中最后一次出现的位置
System.out.println(str.lastIndexOf("in"));
isEmpty() -boolean 判断当前字符串是否为空字符串(长度为0) 是 -true , 不是 -false
System.out.println(str.isEmpty());
replace(String old, String new) - String
将原字符串中old部分 替换 new 得到一个新字符串
System.out.println(str.replace("in", "***"));
contains(String s) -boolean 判断参数是否为原字符串的子串
System.out.println(str.contains("Jaa"));
startsWith(String str) - boolean 判断原字符串是否以参数字符串为前缀
System.out.println(str.startsWith("in"));
substring(int index) -String 从给定下标的位置开始截取原字符串 得到一个新的子串
System.out.println(str.substring(1));
substring(int begin, int end) -String 从给定下标的范围截取原字符串 得到一个新的子串
System.out.println(str.substring(1, 5));
toCharArray() - char[] 将字符串转换为字符数组
String类的内部是由一个final修饰的char[]维护的
char[] ch=str.toCharArray();
toLowerCase() -String 将原字符串中的字母转换为全小写
System.out.println(str.toLowerCase());
toLowerCase() -String 将原字符串中的字母转换为全大写
System.out.println(str.toUpperCase());
String str1=" hello world ";
String str2="hello world";
System.out.println(str1.equals(str2));
System.out.println(str1);
trim() -String 将原字符串两端的空白字符消除掉
System.out.println(str1.trim());
字符串内存
字符串内存.png如果字符串中包含了字符串对象 则该内容的地址在堆中
StringBuffer和StringBuilder
StringBuffer类没有重写Object类中的equals()
所以使用equals()比较两个StringBuffer对象时
比较的仍然是地址
List集合
List集合(接口):有序,可重复集合
List list=new ArrayList();//向上造型
System.out.println(list.size()); 集合当前的元素个数
list.add(12); 集合添加元素
list.add("abc");
list.add("abc");
System.out.println(list.size());
System.out.println(list.get(0)); 提供下标获取元素
System.out.println(list.get(1));
System.out.println(list.get(2));
删除集合中的与参数对象相同的元素
如果参数是整数值时,删除对应下标的元素
list.remove(0);
list.remove("abc");
集合的遍历
List list=new ArrayList();
list.add("hello");
list.add("tom");
list.add("jack");
通过for循环遍历集合
for(int i=0;i<list.size();i++){
System.out.println(list.get(i));
}
for each 遍历集合
* 冒号左边 声明一个对象表示集合中元素(数据类型对应)
* 冒号右边 表示需要遍历的集合对象
* 操作obj 就是操作实际元素
for(Object obj:list){
System.out.println(obj);
}
* 通过迭代器(Iterator接口)遍历集合
*
* hasNext():判断游标的右边是否存在下一个元素
* next():获取游标右边的元素,并且将游标向下移动
*
* 使用迭代器遍历集合的过程中,如果需要删除元素
* 只能使用迭代器提供的删除方法
通过集合的iterator() 获取迭代器
Iterator it=list.iterator();
System.out.println(it.hasNext());
while(it.hasNext()){
System.out.println(it.next());
}
ArrayList 集合底层是由Object[]实现的, 查询元素的效率更高
泛型:保证集合中的数据是相同的对象
List<Double> list=new ArrayList<Double>();
List集合中的元素如果是自定义类型(例 :Person),无法使用Collections.sort() 对该集合中的元素进行排序,因为自定义类本身不存在大小规则。
解决方法: 自定义类实现Comparable<T> ,可以对该类添加比较大小的规则(该类的对象之间比较大小)
public class Person implements Comparable<Person>{
/重写该方法 ,根据类的属性自定义比较大小的规则
@Override
public int compareTo(Person o) {
// TODO Auto-generated method stub
return this.age-o.age;
}
}
Java定义好的类,已经实现了Comparable<T>接口
从而确定了自身比较大小的规则,如果该规则不适用于
我们的程序当中,可以重新定义一个比较器Comparator接口
public class ComparImp implements Comparator<String>{
@Override
public int compare(String s1, String s2) {
// TODO Auto-generated method stub
return s1.length()-s2.length();
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
List<String> list=new ArrayList<String>();
ComparImp c=new ComparImp(); 比较器对象
list.add("哈哈");
list.add("不国人民");
list.add("天真和");
Collections.sort(list,c); 重载方法
for(String s:list){
System.out.println(s);
}
}
Set集合
set集合(接口):不存在重复元素,元素存取的顺序不同
Set<String> set=new HashSet<String>(); 定义方式
set.add("abc"); 添加元素
set.add("哈哈");
set.add("hello");
set.add("abcdef");
set.add("中国");
System.out.println(set.size()); 集合元素个数
System.out.println(set.isEmpty()); 判断集合是否为空
System.out.println(set.contains("中国人"));判断集合是否包含该元素
set.clear();清空集合
遍历集合
Iterator<String> it=set.iterator();
while(it.hasNext()){
System.out.println(it.next());
}
TreeSet集合:会将内部元素按照元素自身的大小规则,升序排序
Map:存储元素以键值对的形式进行存储(key-value) ,其中key不可以重复
Map<String,Integer> map=new HashMap<String,Integer>(); 定义方式
map.put("数学", 90); 添加元素
map.put("英语", 90);
map.put(null,null); map可以使用null作为key
System.out.println(map.get("英语")); 通过key获取value
System.out.println(map.remove("数学")); 通过key删除元素 key-value同时删除
System.out.println(map.size()); 元素个数
System.out.println(map.get("数学")); 如果key不存在,返回结果是null
System.out.println(map.get(null));
System.out.println(map.containsKey("英语")); 判断map中是否包含key
遍历Map的三种方式
Map<String,Integer> map=new HashMap<String,Integer>();
map.put("数学", 90);
map.put("英语", 93);
map.put("Java",92);
keySet() - Set<T> 将Map中所有的key 获取出来放入Set集合中
Set<String> keySet= map.keySet();
通过遍历set集合获取集合中的每一个key
for(String key:keySet){
System.out.println(map.get(key));
}
* map底层实际上维护了一个Entry实例
* Entry提供了两个不同的方法,分别获取key和value
Set<Entry<String,Integer>> entrySet=map.entrySet();
for(Entry<String,Integer> entry:entrySet){
System.out.println(entry.getKey()+":"+entry.getValue());
}
values() 将map中所有的值获取出来 放入Collection中
Collection<Integer> values=map.values();
for(Integer value:values){
System.out.println(value);
}
File 文件类
File f1=new File("D:/a.txt"); 定义方式
File f2=new File("D:/hello");
System.out.println(f1.exists()); 判断文件对象是否存在
System.out.println(f1.length()); 获取文件的字节大小
System.out.println(f1.getName()); 获取文件名
System.out.println(f2.exists());
System.out.println(f2.getName());
System.out.println(f1.getPath());获取文件路径
System.out.println(f2.getPath());
File[] files=f2.listFiles(); 获取目录下的子选项(文件和目录)
System.out.println(files.length);
for(File f:files){
System.out.println(f.getName());
}
public static void main(String[] args) {
// TODO Auto-generated method stub
File f1=new File("D:/b.txt");
try {
boolean flag=f1.createNewFile();
if(flag){
System.out.println("文件创建成功");
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
File f1=new File("D:/hello");
if(f1.mkdir()){
System.out.println("目录创建成功");
}
}
先创建目录 再创建文件
public static void main(String[] args) {
// TODO Auto-generated method stub
//D:/hello/hello.txt
File f1=new File("D:/hello");
File f2=new File("D:/hello/hello.txt");
if(f1.mkdir()){
try {
f2.createNewFile( );
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
File f1=new File("D:/n");
//如果目录中存在子选项的话,无法直接删除该目录
boolean flag=f1.delete();//删除文件和目录
if(flag){
System.out.println("目录删除成功");
}else{
System.out.println("目录删除失败");
}
}
递归方法解决问题
public static void main(String[] args) {
// TODO Auto-generated method stub
将hello目录中的所有子文件以及子目录的文件名打印出来
* listFiles(),isDirectory()
*
* 首先获取一级目录下的所有子项
* 遍历所有子项,如果是目录的话 继续遍历该子目录下的所有子项
* 重复以上操作,直到没有目录为止
File f=new File("D:/hello");
getFile(f);
}
public static void getFile(File file){
File[] files=file.listFiles();
for(File f:files){
if(f.isDirectory()){
System.out.println("目录:"+f.getName());
getFile(f);
}else{
System.out.println("文件:"+f.getName());
}
}
}
字节输入流
public static void main(String[] args) {
// TODO Auto-generated method stub
输入流 :将数据从外部(本地磁盘,网络)读取到程序中的流
FileInputStream 属于字节流
FileInputStream(File f) ,FileInputStream(String url)
File f=new File("D:/hello.txt");
try {
FileInputStream fis=new FileInputStream(f);
byte [] buff=new byte[10];
int len; 表每次读取文件信息的位置
StringBuffer s=new StringBuffer();
while((len=fis.read(buff))!=-1){
new String(buff,0,len) 将字节数组中的内容转换字符串
s.append(new String(buff,0,len));
}
System.out.println(s);
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
字节输出流
public static void main(String[] args) throws IOException {
// TODO Auto-generated method stub
字节输出流
File f=new File("D:/a.txt");
构造器中加入参数 true 表示以追加的形式写入文件内容
如果构造器中的文件路径不存在,则会将文件创建出来
FileOutputStream fos=new FileOutputStream("D:/b.txt",true);
String message="abc";
byte[] buff=message.getBytes();
fos.write(buff); 写出方法
fos.close();
System.out.println("文件写出完毕");
}
文件的复制
public static void main(String[] args) throws IOException {
// TODO Auto-generated method stub
FileInputStream fis=new FileInputStream("D:/内存图.png");
FileOutputStream fos=new FileOutputStream("D:/内存图1.png",true);
byte[] buff=new byte[4];
int len;
while((len=fis.read(buff))!=-1){
fos.write(buff, 0, len);
}
fis.close();
fos.close();
System.out.println("文件复制完毕...");
}
字符流
public static void main(String[] args) throws IOException {
// TODO Auto-generated method stub
InputStreamReader(FileInputStream fis)
对于纯文本文件 字符流的读写效率更高,但是字符流只能读写纯文本文件
long time1=System.currentTimeMillis();当前时间到1970-1-1所经历的毫秒值
FileInputStream fis=new FileInputStream("D:/a.txt");
InputStreamReader isr=new InputStreamReader(fis);字符输入流
char[] cuff=new char[5];
int len;
StringBuffer s=new StringBuffer();
while((len=isr.read(cuff))!=-1){
s.append(new String(cuff,0,len));
}
isr.close();
long time2=System.currentTimeMillis();
System.out.println(time2-time1);
}
字符串输入流的内容 维护了一个字符缓冲区每一次写出的数据并不能直接写入到文件中而是存放在缓冲区中,只有关闭流时,会将缓冲区中的数据 全部写出
public static void main(String[] args) throws IOException {
// TODO Auto-generated method stub
FileOutputStream fos=new FileOutputStream("D:/b.txt",true);
OutputStreamWriter osw=new OutputStreamWriter(fos);
String message="大集合";
osw.write(message);
osw.flush(); 该方法会将缓冲区的内容一次性写出
osw.close();
System.out.println("写出完毕...");
}
高级流 ,处理流
public static void main(String[] args) throws IOException {
// TODO Auto-generated method stub
BufferedInputStream 高级流(处理流) 输入流
FileInputStream fis=new FileInputStream("D:/b.txt");
BufferedInputStream bis=new BufferedInputStream(fis);
byte[] buff=new byte[4];
int len;
StringBuffer s=new StringBuffer();
while((len=bis.read(buff))!=-1){
s.append(new String(buff,0,len));
}
System.out.println(s);
}
public static void main(String[] args) throws IOException {
// TODO Auto-generated method stub
FileOutputStream fos=new FileOutputStream("D:/c.txt");
BufferedOutputStream bos=new BufferedOutputStream(fos);
String message="大家好...";
byte[] buff=message.getBytes();
bos.write(buff);
bos.close(); 方法的内部,调用了flush()
}
实现多线程的方式
- 定义一个类,继承Thread 重写run方法
- 定义一个类,实现Runnable接口 重写run方法
线程的方法
Thread.currentThread() 返回当前正在执行线程对象
getName() 线程名
da.setName("线程da"); 设置线程名.
setPriority(Thread.MAX_PRIORITY);//设置线程优先级方法
start(); 启动线程,启动线程之后会自动调用线程对应的run方法
Thread.sleep(100); 当前线程会睡眠100毫秒之后 ,再继续执行
join() ,在线程A中 调用线程B的join(),则线程A 会等待线程B执行结束
线程的生命周期.png线程的生命周期
线程安全问题
多个线程对象访问同一个资源,导致数据错乱
public class ThreadDemo implements Runnable{
/*
* 多线程存在线程安全问题
* 多个线程对象访问同一个资源,导致数据错乱
*/
static int n=100; //表示100张车票
@Override
public void run() {
while(n>0){
shellNum();
}
}
/*
* synchronized 同步关键字 ,同步方法
* 只有当前进入方法的线程执行完毕后,其他
* 线程才可以进入方法
*/
public synchronized void shellNum(){
if(n>0){
System.out.println(Thread.currentThread()
.getName()+" 卖出了第"+n+"张票");
n--;
}
}
}
网络编程
网络编程的实质就是两个(或多个)设备(例如计算机)之间的数据传输。
为了能够方便的识别网络上的每个设备,网络中的每个设备都会有一个唯一的数字标识,这个就是IP地址。在计算机网络中,现在命名IP地址的规定是IPv4协议,该协议规定每个IP地址由4个0-255之间的数字组成,例如10.0.120.34。每个接入网络的计算机都拥有唯一的IP地址,这个IP地址可能是固定的,例如网络上各种各样的服务器,也可以是动态的,例如使用ADSL拨号上网的宽带用户,无论以何种方式获得或是否是固定的,每个计算机在联网以后都拥有一个唯一的合法IP地址,就像每个手机号码一样
IP地址解决了在网络中找到一个计算机的问题,但是为了让一个计算机可以同时运行多个网络程序,就引入了另外一个概念——端口(port)。
在同一个计算机中每个程序对应唯一的端口,这样一个计算机上就可以通过端口区分发送给每个端口的数据了,换句话说,也就是一个计算机上可以并发运行多个网络程序,而不会在互相之间产生干扰。
在网络通讯中,第一次主动发起通讯的程序被称作客户端(Client)程序,简称客户端,而在第一次通讯中等待连接的程序被称作服务器端(Server)程序,简称服务器。一旦通讯建立,则客户端和服务器端完全一样,没有本质的区别。
例如QQ程序,每个QQ用户安装的都是QQ客户端程序,而QQ服务器端程序则运行在腾讯公司的机房中,为大量的QQ用户提供服务。这种网络编程的结构被称作客户端/服务器结构,也叫做Client/Server结构,简称C/S结构。
网络通信方式
-
TCP(传输控制协议)方式
-
UDP(用户数据报协议)方式
在Java语言中,对于TCP方式的网络编程提供了良好的支持,在实际实现时,java.Net.Socket类代表客户端连接,以java.net.ServerSocket类代表服务器端连接。在进行网络编程时,底层网络通讯的细节已经实现了比较高的封装,所以在程序员实际编程时,只需要指定IP地址和端口号码就可以建立连接了。
客户端
public static void main(String[] args) throws UnknownHostException, IOException {
// TODO Auto-generated method stub
int port=8088;//表示服务端的端口号
String ip="localhost";//表示本机IP
String message="服务端 hello";//项服务器发送的消息
//创建客户端
Socket socket=new Socket(ip,port);
System.out.println("客户端已启动...");
//获取网络输出流 向服务端发送消息
OutputStream os=socket.getOutputStream();
OutputStreamWriter osw=new OutputStreamWriter(os);
osw.write(message);
osw.flush();
//关闭socket
socket.close();
}
服务端
public static void main(String[] args) throws IOException {
// TODO Auto-generated method stub
//创建服务端 设置端口号
ServerSocket ss =new ServerSocket(8088);
System.out.println("服务端已启动...");
//accept(); 是一个阻塞方法 ,该方法在监听是否
//有客户端连接到该服务端,没有客户端连接,该方法会一直阻塞
//该方法可以获取socket对象 用于获取客户端的内容
Socket socket=ss.accept();
System.out.println("客户端已连接...");
//获取网络输入流 读取客户端信息
InputStream is=socket.getInputStream();
InputStreamReader isr=new InputStreamReader(is);
char[] chff=new char[20];
int len;
String message="";
while((len=isr.read(chff))!=-1){
message=new String(chff,0,len);
}
System.out.println("客户端:"+message);
//关闭socket
socket.close();
}
持续交互
public static void main(String[] args) throws UnknownHostException, IOException {
// TODO Auto-generated method stub
Scanner scan=new Scanner(System.in);
int port =8088;
String ip="localhost";
System.out.println("客户端已连接...");
while(true){
Socket socket=new Socket(ip,port);
System.out.println("请发送消息:");
String message=scan.next();//客户端发送给服务端
OutputStream os=socket.getOutputStream();
OutputStreamWriter osw =new OutputStreamWriter(os);
osw.write(message);
osw.flush();
socket.shutdownOutput();//本次输出流结束
if("exit".equals(message)){
socket.close();
break;
}
//接收服务端响应的信息
InputStream is=socket.getInputStream();
InputStreamReader isr=new InputStreamReader(is);
char[] chff=new char[20];
int len;
String mes="";
while((len=isr.read(chff))!=-1){
mes=mes+new String(chff,0,len);
}
System.out.println("服务端回复:"+mes);
}
}
public static void main(String[] args) throws IOException {
// TODO Auto-generated method stub
Scanner scan=new Scanner(System.in);
//创建服务端 设置端口
ServerSocket ss=new ServerSocket(8088);
System.out.println("服务端已启动...");
while(true){
//socket可以获取网络输入流 - 得到客户端的消息
Socket socket=ss.accept();
InputStream is=socket.getInputStream();
InputStreamReader isr=new InputStreamReader(is);
char[] chff=new char[20];
int len;
String message="";
while((len=isr.read(chff))!=-1){
message=message+new String(chff,0,len);
}
if("exit".equals(message)){
socket.close();
break;
}
System.out.println("客户端:"+message);
//对客户端做出响应
OutputStream os=socket.getOutputStream();
OutputStreamWriter osw=new OutputStreamWriter(os);
System.out.println("回复客户端:");
String mse=scan.next();//回复给客户端的内容
osw.write(mse);
osw.flush();
socket.shutdownOutput();
}
}
一个服务端对应多个客户端
public static void main(String[] args) throws IOException {
// TODO Auto-generated method stub
Scanner scan=new Scanner(System.in);
//创建服务端 设置端口
ServerSocket ss=new ServerSocket(8088);
System.out.println("服务端已启动...");
while(true){
//socket可以获取网络输入流 - 得到客户端的消息
Socket socket=ss.accept();
ServerThread st=new ServerThread(socket);
Thread t=new Thread(st);
t.start();
}
}
public class ServerThread implements Runnable{
private Socket socket;
public ServerThread(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
// TODO Auto-generated method stub
start();
}
public void start(){
Scanner scan=new Scanner(System.in);
InputStream is;
try {
is = socket.getInputStream();
InputStreamReader isr=new InputStreamReader(is);
char[] chff=new char[20];
int len;
String message="";
while((len=isr.read(chff))!=-1){
message=message+new String(chff,0,len);
}
if("exit".equals(message)){
socket.close();
return;
}
//getInetAddress() 获取客户端IP
System.out.println("客户端"+socket.getInetAddress()+":"+message);
//对客户端做出响应
OutputStream os=socket.getOutputStream();
OutputStreamWriter osw=new OutputStreamWriter(os);
System.out.println("回复客户端:");
String mse=scan.next();//回复给客户端的内容
osw.write(mse);
osw.flush();
socket.shutdownOutput();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public class ClientA {
public static void main(String[] args) throws UnknownHostException, IOException {
// TODO Auto-generated method stub
Scanner scan=new Scanner(System.in);
int port =8088;
String ip="localhost";
System.out.println("客户端A已连接...");
while(true){
Socket socket=new Socket(ip,port);
System.out.println("请发送消息:");
String message=scan.next();//客户端发送给服务端
OutputStream os=socket.getOutputStream();
OutputStreamWriter osw =new OutputStreamWriter(os);
osw.write(message);
osw.flush();
socket.shutdownOutput();//本次输出流结束
if("exit".equals(message)){
socket.close();
break;
}
//接收服务端响应的信息
InputStream is=socket.getInputStream();
InputStreamReader isr=new InputStreamReader(is);
char[] chff=new char[20];
int len;
String mes="";
while((len=isr.read(chff))!=-1){
mes=mes+new String(chff,0,len);
}
System.out.println("服务端回复:"+mes);
}
}
}
public class ClientB {
public static void main(String[] args) throws UnknownHostException, IOException {
// TODO Auto-generated method stub
Scanner scan=new Scanner(System.in);
int port =8088;
String ip="localhost";
System.out.println("客户端B已连接...");
while(true){
Socket socket=new Socket(ip,port);
System.out.println("请发送消息:");
String message=scan.next();//客户端发送给服务端
OutputStream os=socket.getOutputStream();
OutputStreamWriter osw =new OutputStreamWriter(os);
osw.write(message);
osw.flush();
socket.shutdownOutput();//本次输出流结束
if("exit".equals(message)){
socket.close();
break;
}
//接收服务端响应的信息
InputStream is=socket.getInputStream();
InputStreamReader isr=new InputStreamReader(is);
char[] chff=new char[20];
int len;
String mes="";
while((len=isr.read(chff))!=-1){
mes=mes+new String(chff,0,len);
}
System.out.println("服务端回复:"+mes);
}
}
}
内部类
无论外部类是否继承其他类,不会影响内部类的继承关系
从而解决了Java中单一继承的缺点
public class Outer1{
private String name="Outer1";
private int out1=10;
static int out=11;
public void say(){
System.out.println("Outer");
调用内部的属性和方法 需要创建内部类对象
Inner inner=new Inner();
inner.getInner();//调用的内部类方法
}
内部类 无法声明static属性/方法
可以声明 static final修饰的属性
可以访问外部类的属性和方法(包含 private ,static)
class Inner extends{内部类
private int i=5;
static final int inn1=6;
String inName="inner";
public void getInner(){
System.out.println("inner");
System.out.println(name);
System.out.println(out);
}
}
}
public class Test1 {
public static void main(String[] args) {
// TODO Auto-generated method stub
外部类名.内部类名 内部类对象 = 外部类对象.new 内部类构造器;
Outer1 out1=new Outer1();
Outer1.Inner inner=out1.new Inner();
System.out.println();
}
}