java基础实习篇

问题:

修饰符分为权限修饰符和非权限修饰符

public protected ,缺省 , private

static ,final ,abstract

在这里插入图片描述

基本数据类型只是数字,什么也干不了,需要使用包装类型,可以查看长度,

public class Main{
    public static void main(String [] args){
        System.out.println("The size of short is "+Short.BYTES+" bytes.");
        System.out.println("The size of short is "+Short.SIZE/8+" bytes.");
        
        
        System.out.println("The size of int is "+Integer.BYTES+" bytes.");
        System.out.println("The size of int is "+Integer.SIZE/8+" bytes.");
        
        
        System.out.println("The size of long is "+Long.BYTES+" bytes.");
        System.out.println("The size of long long is "+Long.BYTES+" bytes.");
    }
}
十进制转化为八进制
public class Main{
    public static void main(String [] args){
        Integer a = 1234;
        System.out.printf("0"+"%o",a);  //八进制
      	System.out.printf(Integer.toOctalString(a));
        System.out.printf(" 0X"+"%X",1234);  //十六进制
      	System.out.printf(Integer.toHexString(a));
    }
}

next(),不能获得带空格的字符串

nextLine(),方法可以获得带空格的字符串

最大公约数,最小公倍数
  • 最大公约数 不能大于 最小值(取模等于零),使用break否则就输出所有的
  • 最小公倍数 不能小于最大值,不能大于 两数之和,使用break否则就输出所有的值了
import java.util.*;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        System.out.println("输入第一个正整数:");
        int m = sc.nextInt();
        System.out.println("输入第二个正整数:");
        int n = sc.nextInt();
        if (m <n){
            int temp = m;
            m = n;
            n = temp;
        }
        //最大公约数
        for (int i = n;i>=1;i--){
            if(m % i ==0 && n % i ==0){
                System.out.println("最大公约数:" + i);
                break;  //一旦在循环中使用break,退出循环
            }
        }
        //最小公倍数
        for (int i = m;i<=m*n;i++ ){
            if (i % m == 0 && i % n == 0){
                System.out.println("最大公倍数:"+i);
                break;
            }
        }
    }
}
水仙花数
  • 三位数的百位数的三次方+十位数的三次方+个位数的三次方 == 这个三位数

  • 表示平方的为pow(2,3) ----->2^3

public class Main {
    public static void main(String[] args) {
        for (int i = 100 ;i <= 999;i++ ){
            int ge = i % 10;
            int shi = (i / 10) % 10;
            int bai = (i / 100);
            //System.out.println(bai+shi+ge);
            int shui =(int) ((Math.pow(ge,3))+(Math.pow(shi,3)+Math.pow(bai,3)));
            if (i == shui){
                System.out.println(shui);
            }
        }
    }
}
输入一堆数,判断正数个数和负数个数,输入0时结束
 public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int positiveNumber = 0;
        int negativeNumber = 0;
        while (true) {
            int a = sc.nextInt();
            if (a > 0) {
                positiveNumber += 1;
            } else if (a < 0) {
                negativeNumber += 1;
            } else {
                break;
            }
        }
        System.out.println(positiveNumber);
        System.out.println(negativeNumber);
}
九九乘法表
public static void main(String[] args) {
        for (int i =1;i<=9;i++){
            for (int j = 1;j<=i;j++){
                System.out.print(j +"*"+ i + "=" +i*j+" ");
            }
            System.out.println();
        }
    }
遍历一百以内的质数()
  • 质数是指在大于1的自然数中,除了1和它本身以外不再有其他因数的自然数。
public static void main(String[] args) {
        boolean isFlag = true;  //判断是否除尽的标志
        long start = System.currentTimeMillis();
        for (int i =2;i<=100;i++){ //遍历2--100
            //for (int j = 2; j < i;j++){ //内层范围(2--i-1)
              for (int j = 2; j <= Math.sqrt(i);j++){  //优化2
                if (i%j==0){      //如果能够除尽将isFlag状态改为false
                    isFlag = false;
                    //break;                     //优化1
                }
            }
            if (isFlag == true){   //输出没有改变的
                System.out.println(i);
            }
            isFlag = true;   //将状态改为true
        }
        long end = System.currentTimeMillis();
        System.out.println("花费时间为:" + (end-start)); //14290     //1601    //258
    }
完数
  • 一个数恰好等于因子之和,这个数就为“完数”,6 = 1 + 2 + 3
public static void main(String[] args) {
        int sum = 0;
       for (int i = 1;i<=1000;i++){
           for (int j = 1;j <= i/2;j++){    j<i/2
               if (i%j==0){
                   sum +=j;
               }
           }
           if (sum == i){
               System.out.println(i);
           }
           sum = 0;
       }
    }
杨辉三角
  • 使用二维数组的内层循环时,应该先赋值,

  • 在赋值循环里将各个值填入

  • 重点在于最后只是单纯的循环二维数组里的值,

  • 				int [][] arr= new int[10][];
            for (int i = 0;i<arr.length;i++) {
                arr[i] = new int[i + 1];//使用二维数组先把空间开辟
                arr[i][0] = 1;
                arr[i][i] = 1;
                if (i >= 2) {
                    //赋值
                    for (int j = 1; j < arr[i].length - 1; j++) {
    //                    arr[i][j] = arr[i-1][j] + arr[i-1][j-1];
                        arr[i][j] = arr[i-1][j] + arr[i-1][j-1];
                    }
                }
            }
            //遍历二维数组
            for (int i = 0;i<arr.length;i++){
                for (int j = 0;j<arr[i].length;j++){
    
                    System.out.print(arr[i][j]+" ");
                }
                System.out.println();
            }
    
一维数组赋值操作
  • 创建一个长度为6的int型数组,要求数组元素的值在1~30之间,且是随机赋值。同时要求各元素的值不同.

  • 赋值操作时最后遍历数组

    				int arr[] = new int[6];
            for (int i = 0;i<arr.length;i++){
    
                arr[i] = (int) (Math.random() * 30) +1;
    
                for (int j = 0;j<i;j++){
                    if (arr[i] == arr[j]){
                        i--;      //如果第二个值和第一个值相同,直接跳出然后重新赋值
                                    //可能需要和前边多次比较
                        break;
                    }
                }
            }
            for (int i = 0;i<arr.length;i++){
                System.out.println(arr[i]);
            }
    
array1赋值array2
  • 堆中只存放这一组数据,
  • new一次只有一个数组
				int[] array1,array2;
        array1 = new int[]{2,3,5,7,11,13,17,19};

        for (int i = 0; i < array1.length; i++) {
            System.out.print(array1[i]+" ");
        }
        System.out.println();
        array2 = array1;   //这个相当于把地址给了array2,
        for (int i = 0; i < array2.length; i++) {
            if (i%2==0){
                array2[i] = i;
            }
            System.out.print(array2[i]+" ");
        }
        System.out.println();
        for (int i = 0; i < array1.length; i++) {
            System.out.print(array1[i]+" ");
        }

//        2 3 5 7 11 13 17 19
//        0 3 2 7 4 13 6 19
//        0 3 2 7 4 13 6 19
数组的反转
  • 一定要记得除以2,否则就会全部遍历一遍,数组又换回来了
				int[] array1;
        array1 = new int[]{2,3,5,7,11,13,17,19};
        for (int i = 0; i < array1.length/2; i++) {
            int temp = array1[i];
            array1[i] = array1[array1.length-1-i];
            array1[array1.length-1-i] = temp;
        }
        for (int i = 0; i < array1.length; i++) {
            System.out.print(array1[i]+" ");
        }
二分法
  • 要求必须有顺序
				int[] array1;
        array1 = new int[]{2,3,5,7,11,13,17,19};
        int description = 4;
        int head = 0;
        int end = array1.length-1;

        boolean flag = true;
        while (head <= end){
            int middle = (head + end)/2;

            if (array1[middle] == description){
                flag = false;
                System.out.println("找到了:"+middle);
                break;
            }else if (description < array1[middle]){  //
                end = middle-1;
            }else{
                head = middle + 1;   //
            }
        }
        if (flag){
            System.out.println("没有这个数据!");
        }
冒泡排序
  • 相邻两个数之间进行比较,然后交换数据

  • 				int[] array1;
            array1 = new int[]{2,3,18,7,34,13,1,17,19};
            System.out.print("遍历前:");
            for (int i : array1){
                System.out.print( i +" ");
            }
            System.out.println();
            for(int i = 0;i < array1.length-1;i++){        //外层控制次数
                for (int j = 0;j <array1.length-i-1;j++){  //内层用来交换数据
                    if (array1[j]>array1[j+1]){            //如果前边这个比后边这个大就交换
                        int temp = array1[j];
                        array1[j] = array1[j+1];
                        array1[j+1] = temp;
                    }
                }
                System.out.print("第"+(i+1)+"次排序:");
                for (int j = 0; j < array1.length; j++) {
                    System.out.print(array1[j] + " ");
                }
                System.out.println();
            }
                System.out.print("冒泡排序后:");
                for (int j = 0; j < array1.length; j++) {
                System.out.print(array1[j]+" ");
            }
    
对象数组
  • new 20个Student对象
  • 随机对年级,分数赋值
  • 将三年级的输出
  • 按成绩进行冒泡排序
  • 引用数据类型的值,要么是null,要么是地址值;数组的情况,意思就是你要是想用它就必须自己实例化
public class Main {

    public static void main(String[] args) {
       Student [] students = new Student[20];//仅仅是分配了一个null值的空间
       for (int i =0;i<students.length;i++){
           students[i] = new Student();                                  //new了20个对象
           students[i].number = i + 1;                                   //[1,20]
           students[i].state = (int) (Math.random() * (6 - 1 + 1) +1);   //[0,6]
           students[i].score = (int) (Math.random() * (100 - 0 +1) + 0); //[0,100]
           students[i].info();
       }
        System.out.println("*****************************");
        for (int i =0;i<students.length;i++){
            if (students[i].state == 3 ){
                students[i].info();
            }
        }
        //按成绩冒泡排序
        for (int i =0;i<students.length;i++){
            for (int j = 0;j < students.length-i-1;j++){
                if (students[j].score>students[j+1].score){
                    Student temp = students[j];               //temp的类型应该为Student对象
                    students[j] = students[j+1];
                    students[j+1] = temp;
                }
            }
        }
        System.out.println("遍历后:");
        for (int i =0;i<students.length;i++){
            students[i].info();
        }
    }
}
class Student{
    int number;
    int state;
    int score;
    public void info(){
        System.out.println("学号:" + number + "年级:" + state + "分数:" + score);
    }
}

改造后:

public class Main {

    public static void main(String[] args) {
       Student [] students = new Student[20];
       for (int i =0;i<students.length;i++){
           students[i] = new Student();                                  //new了20个对象
           students[i].number = i + 1;                                   //[1,20]
           students[i].state = (int) (Math.random() * (6 - 1 + 1) +1);   //[0,6]
           students[i].score = (int) (Math.random() * (100 - 0 +1) + 0); //[0,100]
           students[i].info();
       }
        System.out.println("*****************************");
       Main main = new Main();   //方法定义在了Main中,所以想要使用就必须实例Main
        //遍历
        main.print(students);
        //年级为3年级的
        System.out.println("******************");
        main.san(students,3);
        //按成绩冒泡排序
        main.sort(students);
        System.out.println("遍历后:");
        main.print(students);

    }
    public void print(Student[] students){
        for (int i =0;i<students.length;i++){
            students[i].info();
        }
    }
    public void san(Student[] students,int state){
        for (int i =0;i<students.length;i++){
            if (students[i].state ==  state){
                students[i].info();
            }
        }
    }
    public void sort(Student[] students){
        for (int i =0;i<students.length;i++){
            for (int j = 0;j < students.length-i-1;j++){
                if (students[j].score>students[j+1].score){
                    Student temp = students[j];               //temp的类型应该为Student对象
                    students[j] = students[j+1];
                    students[j+1] = temp;
                }
            }
        }
    }
}
class Student{
    int number;
    int state;
    int score;
    public void info(){
        System.out.println("学号:" + number + "年级:" + state + "分数:" + score);
    }
}

对象数组的内存解析:

在这里插入图片描述

实现trim()方法
 String s = "  a bc de  ";
        System.out.println(s.length());
        System.out.println(return1(s));
        System.out.println(return1(s).length());
    }

    public static String return1(String string){
        int start = 0;
        int end = string.length()-1;
        for (start = 0; start < string.length() && start <= end; start++){
            char s = string.charAt(start);
            if (s == ' '){
                start++;
            }else {
                break;
            }
        }

        for (end = string.length() - 1; end>=0 && end >= start; end--){
            char s = string.charAt(end);
            if (s == ' '){
                end--;
            }else {
                break;
            }
        }
        String s2 = string.substring(start,end+1);
        return s2;
    }
实现指定字符串的反转
public static void main(String[] args) {

        String s = new String("abcdefg");
        System.out.println(s);
        reserve(s,2,5);
        System.out.println(reserve(s,2,6));
    }
    public static String reserve(String str,int index,int end){

        String s = str.substring(index,end);

        StringBuffer stringBuffer = new StringBuffer(s);
        StringBuffer stringBuffer1 = new StringBuffer();

        stringBuffer1.append(str.substring(0,index));
        stringBuffer1.append(stringBuffer.reverse());
        stringBuffer1.append(str.substring(end,str.length()));
        String s1 = new String(stringBuffer1);
        return s1;
    }
返回指定字符串在指定字符中出现的次数
public static void main(String[] args) {
        String s1 ="ab";
        String s2 = "abaablksjdgkjasdabdkflsabjf";

        System.out.println(getCount(s2, s1));
    }
    public static int getCount(String s1,String s2){
        int index = 0;
        int count = 0;
        if (s1.length() >= s2.length()){
            while (s1.indexOf(s2)!=-1){
                index = s1.indexOf(s2);
                s1 = s1.substring(index + s2.length());
                count++;
            }
            return count;
        }else {
            return 0;
        }
    }

变量:

A = 65,a = 97

包含变量类型,变量名和存储的值(int a = 0)

java中每个变量必须先声明,后使用

八种基本数据类型:byte,short,int,long,float,double,boolean,char

引用数据类型:类(class),接口(interface),数组(【】)

按声明位置不同分为成员变量和局部变量

  • 在方法体外,类体内声明的变量称为成员变量。
  • 在方法体内部声明的变量称为局部变量

成员变量:

  • 实例变量(不以static修饰)
  • 静态变量(以static修饰)

整数类型:(定义long时,必须以“l”或“L”结尾,且结果不显示后缀l)

  • byte 1字节=8bit位 -128——127 256个
  • short 2字节 -215——(215)-1 2^16个
  • int 4字节 -231——(231)-1 2^32个
  • long 8字节 -263——(263)-1 2^64个

浮点型:(定义float时,必须以”f“或”F“结尾,且后缀不带f)

  • 单精度float 4字节
  • 双精度double 8字节

字符型:

  • char 1字符==2字节 char进行运算的时候是数字,否则就是字符本身
  • 定义char型变量,通常使用一对 ’ ‘,且只能存放一个字符,不能不放
  • 转义字符 char c1 = ‘\n’;
  • \ 表示转义字符, / 表示根目录

布尔类型:

  • 只有true和false
System.out.println("你参加不了单身party了!")  \\你参加不了单身party了!
    
System.out.println("你参加不了\n单身party了!")  \\你参加不了
    										  \\单身party

System.out.println("你参加不了\\n单身party了!");  \\你参加不了\n单身party了!
    
System.out.println("你参加不了\"单身\"party了!"); \\你参加不了"单身"party了!

基本数据类型之间的运算规则:(整型常量默认int,浮点型默认double)

1.自动类型提升:

  • 当容量小的数据类型的变量与容量大的数据类型进行运算时,结果自动提升为容量大的数据类型。
  • byte、char、short–>int–>long–>float–>double
  • 特别的:当byte、char、short三种类型的变量做运算时,结果为int型

2.强制类型转化

  • 				byte b = 127 ;
           b++;
           System.out.println(b);  //-128  看二进制
    

String类型:

  • String属于引用数据类型,字符串

  • 声明String类型变量时,使用一对“”

  • String可以和八种基本数据类型做运算,且运算只能是连接运算

  • 运算的结果仍是String

  • String s = 123;    //no
          String s2 = 123 +"";  //"123"
    		String s1 = "Hello";
    		int num = 10;
    		char c = 'a';
    		System.out.println( s1 + num + c);//Hello10a
    		System.out.println(num + c +s1);//107Hello
    		System.out.println(c + s1 + num);//aHello10
    
  • //因为char类型默认要转化为int
    		char c1 = '	';
    		System.out.println("*" + c1 + "*");		//*	*
    		System.out.println("*	*");			//*	*
    		System.out.println('*' + '\t' + '*');	//93
    		System.out.println('*' + "\t" + '*');	//* *
    		System.out.println("*" + '\t' + '*');	//* *
    		System.out.println('*' + '\t' + "*");	//51*
    

二进制: 以0b或0B开头

  • 最前边的一位是符号位(0表示正数,1表示负数)

  • 正数:原码,补码,反码都是一样的

  • 负数:

  • 1 0001110    -14的原码   原码到反码为按位取反
    1 1110001    -14的反码   反码到补码为加 1 
    1 1110010    -14的补码   
    
  • 计算机都是以补码方式存储数据

八进制: 以0开头

十六进制:以0X或0x开头

运算:

取模时,结果符号与分子相同

 				int a = -23;
        int b = 13;
        int c = -12;
        System.out.println(a%b);  //-10
        System.out.println(b%c);  //1
        int a = 10;
        int b = 3;
        double sum = (double)a/b  //3.4

++a 先++,后运算

		short s1 = 10;
		s1++;  //自增一不会改变本身变量的数据类型

		byte b1 = 127;
		b1++;  //-128

&逻辑与 ,&&短路与

  • &左边不正确后边还要走,&&左边不正确右边不运算

<<

int i = 21;
System.out.println(i<<3)     21*2^3
System.out.println(i>>3)     21/2^3

三元运算符:

(条件表达式)?表达式1:表达式2;

表达式1和表达式2为同种类型,如果不是同种类型但存在自动提升也可以

Object object  = true?new Integer(1):new Double(2.0);     //自动类型提升
        System.out.println(object);  //1.0

Scanner:

  • 输入多组数据时

  • System.out.println("请输入第一个数:");
    int a = sc.nextInt();
    System.out.println("请输入第二个数:");
    int b = sc.nextInt();
    System.out.println("请输入第三个数:");
    int c = sc.nextInt();
    

Math:

  • 0.0<=Math.random()<1.0 返回的类型为double类型 ,[0.0,1.0)

  • 取值范围10~99

  • Math.random()*100 范围为[0.0,100.0)

  • Math.random()*90 [0.0,90.0)

  • Math.random()*90 + 10 [10.0,100.0)

  • [a,b] (int)(Math.random()*(b-a+1)+a)

Switch:

  • 根据switch表达式的值,依次匹配各个case中的常量,一旦匹配成功,则进入相应的case中,调用执行语句。
  • 当执行完语句后,则仍然依次执行其他的case语句,直到遇到break或程序执行完毕
  • switch结构中的表达式,只能是6种数据类型(byte,short,char,int,枚举,String),不能是浮点型,布尔类型

循环结构:

  • 初始化条件,循环条件,循环体,迭代条件

  • for(①;;④)
    {;
    }
    
  • public static void main(String[] args) {
            int num = 1;
    for(System.out.print('a');num<=3;System.out.print('c'),num++)
            System.out.print('b');//abcbcbc
        }
    
  • while(){
        ③
        ④
    }
    
  • do{
       ③
       ④
    }while()
    
  • do-while至少执行一次循环体

  • 执行多次的时候while和do-while没什么区别

break和continue和return

  • break 在 循环结构和switch-case中 使用 结束当前循环
  • continue 在 循环结构中 使用 结束本次循环
  • return在 方法体内 使用 结束本次方法,如果有返回值,返回数值
  • break和continue,return之后不能声明执行语句
 public static void main(String[] args) {
        for (int i =1;i<= 10;i++){
            if (i%4==0){
                //break;   //123
                continue;  //123567910
            }
            System.out.print(i);
        }
    }

数组:

  • (数组一旦初始化,长度就是确定的)

一维数组

  • 是多个相同类型数据按照一定顺序排列的集合,并使用一个名字命名,并通过编号的方式对数据进行统一管理。

  • 数组是引用数据类型,创建数组可以是基本数据类型也可以是引用数据类型

  • 创建数组对象会在内存中开辟出一整块连续的空间,而数组名引用的是这块连续空间的首地址

  • 数组长度一旦确定就不会被修改

  • 静态初始化和动态初始化(赋值的叫静态没赋值叫动态)

    int []ids = new int[]{1001,1002,1003,1004};//静态初始化
    int []ids = new int[5];//动态初始化
    
  • char[] arr 默认是0不是‘0’;

  • 		  int [] arr= new int[4];
           System.out.println(arr);  //[I@7ea987ac
           System.out.println(arr[0]);  //0
    
  • boolean类型默认值为false

在这里插入图片描述
在这里插入图片描述

二维数组

  • 当没有赋值时
			int [][] arr= new int[4][3];
			1.外层默认值为地址    内层默认值为该数据类型的默认值
      int [][] arr1 = new int[4][];
			内层默认值为null,因为是个数组
      外层空指针异常


				int [][] arr= new int[4][3];
        System.out.println(arr);        //[I@7ea987ac
        System.out.println(arr[0]);     //[I@12a3a380
        System.out.println(arr[0][0]);  //0

        //没指名行值时
        int [][] arr1 = new int[4][];
        System.out.println(arr1);       //[[I@29453f44
        System.out.println(arr1[0]);    //null   因为数组是一个引用数据类型,所以内部时null,二维数组里存放的时数组,
        System.out.println(arr1[0][1]); //NullPointerException

在这里插入图片描述

arrays工具类

在这里插入图片描述

面向对象:

在这里插入图片描述

堆:存放的时实例对象

栈:就是虚拟机栈,用于存储局部变量

方法区:常量池,静态方法区,类信息。

每个线程用于自己独立的:栈,程序计数器

多个线程,共享同一个进程的:方法区,堆

Java类及类成员:
基础:
  • (属性,方法,构造器,代码块,内部类)
  • 类:对一类事物的描述,是抽象的,概念上的定义;
  • 对象:是实际存在的该类事物的每个个体,因为也被称为实例(instance)
  • 面向对象程序设计的重点是类的设计,设计类就是设计类成员
操作:

局部变量和成员变量的区别:

  • 相同点:

    • 定义变量的格式 数据类型 变量名 = 变量值
    • 先声明,后使用
    • 变量都有其自己的作用域
  • 不同点:

    • 在类中声明的位置不同
      • 局部变量:方法内,方法形参,代码块,构造器,构造器形参
      • 成员变量:定义在一组类{}中
    • 关于权限修饰符的不同:
      • 成员变量:可以在声明成员变量时,指明权限,常用的权限修饰符:private,缺省,protected,public
      • 局部变量不能使用权限修饰符
    • 默认初始化值:
      • 成员变量:byte,char,short,int,long = 0,float,double = 0.0,boolean = false
      • 局部变量:没有默认的初始化值,使用前,必须先赋值。调用形参时,在方法赋值就可以
    • 在内存中的位置:
      • 成员变量:加载在堆内存中(非static)
      • 局部变量:加载在栈空间中
  • 引用数据类型的值,要么是null,要么是地址值;数组的情况,意思就是你要是想用它就必须自己实例化

  • 匿名对象的使用

    • new 对象();创建一个对象的时候没有显式的给他一个变量名。即匿名对象
    • 匿名对象只能调用一次
  • 方法的重载:

    • 同一个类中,允许出现一个以上同名的方法,只要他们的参数类型参数个数不同即可。
    • 判断是否重载:跟方法的权限修饰符返回值类型形参方法体都没有关系
  • 重写:子类可以根据需要对从父类中继承的方法进行改造,在程序执行时,子类方法将覆盖父类的方法

    • 形参列表相同
    • 子类方法的权限修饰符不小于父类被重写的方法的权限修饰符,子类不能重写父类中声明为private权限的方法
    • 父类返回值类型为。。。,子类重写时,可以声明为Object
    • 子类和父类的同名同参数的方法要么都声明为非static(考虑重写),static(不是重写)
  • 值传递:

    • 如果参数类型时基本数据类型,此时实参赋给形参的是实参真是存储的数据值。
    • 如果参数类型时引用数据类型,此时实参赋给形参的是实参真是存储的地址值。
    • 基本数据类型的数据值出了规定范围就没有了,引用的是对地址里的值进行改变

    在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

public static void main(String[] args) {
        int[] arr = new int[20];
        System.out.println(arr);
        char[] arr1 = new char[]{'a','b'}; 
        System.out.println(arr1); //ab而不是0,因为定义了一个char【】的方法
    }
面向对象的特征:

在这里插入图片描述

  • (封装,继承,多态)(抽象)
封装性的体现
  • 我们将类的属性私有化(private),同时,提供公共的(public)的接口去获取getXxx()去获取它设置它setXxx(),

  • public int  getXxx(){  //获取值是通过返回值,所以有返回值类型,没有参数
      return a;
    }
    public void setXxx(int a){ //set是设置值,需要接受外边传来的值,不需要返回
      a = 1;
    }
    
  • 不对外暴露私有的方法

  • Java封装性的体现需要权限修饰符配合

  • 修饰类的话只能使用缺省和public,内部类可以使用private

  • 构造器:(创建对象Person p = new Person(),初始化对象属性)

  • 构造器不属于方法,因为方法是可以被对象调用的,而构造器不能

继承
  • 一旦子类A继承了父类B,A就获取了B的所有的属性、方法

    特别的,父类声明为private的属性和方法,子类继承父类后,仍然获得了父类中私有的结构,只是因为封装性的影响,使得子类不能直接调用父类的结构而已。

  • 父类中的属性声明为private时,使用这个属性时,其实是有它,只是想要调用属性,只能使用setXxx()或getXXX()

  • 一个类可以被多个子类继承,但一个类只能继承一个父类

  • 所有的类都直接或间接继承object类,就算只继承了B,但是父类B继承了Object类

多态:
  • 子类Man必须继承父类Person,而且必须重写父类中的部分方法

  • 多种形态,Person p = new Man();

  • 编译期只能调用父类声明的方法,不能调用子类中的方法(在子类中定义的方法,并不能被使用,但其实内部时加载的有),在运行期输出的是子类中重写过的数据

  • 编译看左边,运行看右边

  • 只适用于方法,不适用于属性

  • 想要运行子类中定义的方法,得强制类型转化

public class Main {

    public static void main(String[] args) {
        Person p =new Person();
        p.eat();   //人吃
        p.walk();   //人走
        Man m = new Man();
        m.eat();  //男人吃
        m.walk();   //男人走
        m.play();  //男人玩

        //多态体现1
        Person p1 = new Man(); //内部其实加载了类Man中的play()方法,只是现在不能被调用
        Man man =(Man) p1;   //想要使用子类中自己的方法,就的强制类型转换
        p1.walk();  //男人走
        p1.eat();   //男人吃
        int age = p1.age;  //调用的是Person中的变量
        System.out.println(age);   //10
        //p1.play();   //不能被调用
        //p1只能使用父类中
        System.out.println("***********");
        //多态体现2,如果想直接调用子类方法
        Main main = new Main();
        main.fun(new Person());
        main.fun(new Man());
        main.fun(new Women());
        //多态体现2
    }
    public void fun(Person p){  //Person p = new XXX();
       p.eat();
       p.walk();
    }
    //多态体现2
    //如果没有多态性,就得重新写一个重载的方法
//    public void fun(Man man){
//        man.eat();
//        man.walk();
//    }
}
class Person {
    int age = 10;
    public void eat(){
        System.out.println("人吃");
    }
    public void walk(){
        System.out.println("人走");
    }
}
class Man extends Person{
    int age = 20;
    public void eat(){
        System.out.println("男人吃");
    }
    public void walk(){
        System.out.println("男人走");
    }
    public void play(){
        System.out.println("男人玩");
    }
}


class Women extends Person{
    public void eat(){
        System.out.println("女人吃");
    }
    public void walk(){
        System.out.println("nv人走");
    }
    public void play(){
        System.out.println("女人玩");
    }
}


向下转型:

有了对象的多态性以后,内存中实际上是加载了子类特有的属性和方法,但是由于声明的变量类型为父类,导致编译时只能调用父类中的方法,子类特有的方法和属性不能调用,如何使用子类中的方法,使用向下转型

目的就是想比较子类对象中自己的属性,就是说重写equals()方法

 public boolean equals(Object obj) {
        if (this == obj){  
            return true;
        }
        if (obj instanceof Person){        //判断是否是Person类
            Person person = (Person) obj;  //强制转换
            return this.age == person.age && this.name.equals(person.name); //因为name是String类型
        }
        return false;
    }
==和equals()方法的区别
  • ==比较的是基本数据类型的时候比较的是值(不一定类型相同)

  • ==比较的是引用数据类型的,比较两个对象的地址值

  • equals()方法只能使用在引用数据类型中,不能使用在基本数据类型中,可以使用在包装类型中。

  • Object类中equals()和==相同

  • 像String,Date,包装类等都重写了equals()方法,比较的不是两个引用地址是否相同,而是比较的值

  • 				Person p =new Person();
            Person p2 =new Person();
            System.out.println(p.equals(p2));  //false,因为这个是继承自父类object中的equals()方法,比较的是地址值
            String s = new String("小明");
            String s1 = new String("小明");
            System.out.println(s.equals(s1));  //true,因为重写了类object中的equals()方法,比较的是值
    
  • 重写equals()方法

  • obj instanceof Person这个obj可以是子类或者本身类

  • public class Main {
    
        public static void main(String[] args) {
            Person p =new Person(12,"小黄");
            Person p2 =new Person(12,"小黄");
            System.out.println(p.equals(p2));  //truee,因为这个是继承自父类object中的equals()方法,比较的是地址值
            String s = new String("小明");
            String s1 = new String("小明");
            System.out.println(s.equals(s1));  //true,因为重写了类object中的equals()方法,比较的是值
    
        }
    }
    class Person {
        int age;
        String name;
        public Person() {
        }
    
        public Person(int age, String name) {
            this.age = age;
            this.name = name;
        }
        public boolean equals(Object obj) {
            if (this == obj){
                return true;
            }
            if (obj instanceof Person){        //判断是否是Person类,
                Person person = (Person) obj;
                return this.age == person.age && this.name.equals(person.name); //因为name是String类型
            }
            return false;
        }
    }
    
toString()
  • 自定义的类没有哦重写toString(),所以输出的是地址
  • String,Date,File重写过toString()方法,所以比较的是值
System.out.println(p2.toString());     //org.example.Person@1b6d3586 地址值因为没有重写toString方法,
System.out.println(s.toString());  //小明  因为String重写了toString()方法
包装类

在这里插入图片描述

        Integer i = new Integer(1);
        Integer i2 = new Integer(1);
        System.out.println(i == i2);  //false
        Integer m = 1;
        Integer n = 1;
        System.out.println(m == n);  //true
        Integer x = 128;
        Integer y = 128;
        System.out.println(x == y);  //false
        //因为Integer内部封装了一个缓存的东西,把-128~127之间的数放入,
        //大于127的就相当于new Integer()
    Integer x = 128;
    Integer y = 128;
    System.out.println(x == y);  //false
    //因为Integer内部封装了一个缓存的东西,把-128~127之间的数放入,
    //大于127的就相当于new Integer()
其他关键字:
  • this,super,static,final,abstract,interface,package,import

  • this可以调用属性,方法,调用构造器,必须在第一行写this();

  • super可以调用属性和方法(和this差不多,可以混合使用),调用构造器必须写在第一行且super和this只能出现一个,没有写就默认调用super()

static:

不论new多少个对象,某些特定的数据我们只需要内存中只有一份,只要修改一处,其余都会改变

static可以修饰成员变量,方法,代码块,内部类,不能用来修饰局部变量

类中属性分为静态变量和实例变量

静态变量随着类的加载而加载,放在方法区的静态域,高于对象的创建,由于类只会加载一次,所以,静态变量只有一份

  • 静态变量:被static修饰,且该类和该类的子类都共享这一个变量,如果改变就全部改变,使用变量名.静态变量或者类.静态变量
  • 实例变量:都独立的拥有一套自己的非静态属性,改变一个值的话,别的实例对象并不会被改变,只能使用变量.静态变量
public class Main {
    public static void main(String[] args) {
        Person p =new Person(12,"小黄");
        Person p2 =new Person(12,"小黄");
        Person1 person1 = new Person1();
        person1.nation = "地球";
        //p.nation = "地球人";
        System.out.println(p.nation);//中国          地球
        System.out.println(p2.nation);//中国			   地球
        System.out.println(Person.nation);//中国	   地球
        System.out.println(person1.nation);//中国	   地球
    }
}
class Person {
    int age;
    String name;
    static String nation = "中国";
    public Person() {
    }

    public Person(int age, String name) {
        this.age = age;
        this.name = name;
    }
}
class Person1 extends Person{
    int age;
    String name;
    public Person1() {
    }

    public Person1(int age, String name) {
        this.age = age;
        this.name = name;
    }
}

在这里插入图片描述

  • 静态方法:被static修饰,内部只能调用静态方法和变量,静态方法随着类的加载而加载类.static变量名.static,不能使用this和super
  • 非静态方法:内部可以调用静态和非静态的方法和变量
		private int id;
    private static int init = 1001;
    public Person() {
        id = init++;
    }
		public Person(int age, String name) {
        this.age = age;
        this.name = name;
        id = init++;
    }
//调用无参的方法创建对象
				Person p =new Person();
        Person p2 =new Person();
        System.out.println(p.getId());  //1001
        System.out.println(p2.getId()); //1002
//调用有参的方法创建对象
				Person p =new Person(12,"小明");
        Person p2 =new Person(12,"小明");
        System.out.println(p.getId());  //1001
        System.out.println(p2.getId()); //1002
代码块:
  • 代码块用来 初始化类、对象,如果修饰只能使用static
  • 静态代码块:
    • 内部可以有输出语句
    • 随着类的加载而执行,且只能执行一次。
    • 作用:初始化类的信息
    • 如果一个类中定义了多个静态代码块,按声明顺序执行
    • 可以放静态变量,静态方法,不能存放成员变量,因为成员变量随着对象的加载而加载,这时候还没有成员变量
  • 非静态代码块:
    • 内部可以有输出语句
    • 随着对象的创建而执行
    • 每创建一个对象,就执行一次非静态代码块
    • 作用:可以在创建对象是,对对象进行初始化
    • 如果一个类中定义了多个非静态代码块,按声明顺序执行
对属性赋值的位置:

①默认初始化

②显示初始化/在代码块中赋值

③构造器初始化

④对象.属性或对象.方法

final:
  • final可以修饰类,方法、变量
  • final修饰类时,此类不能被继承
  • final修饰方法,子类就不能取重写该方法
  • final修饰变量:此时,变量就是一个常量,修饰基本数据类型的变量,值不能被改变,修饰引用数据类型的变量表示地址不能被改变

static 和 final 能一起修饰属性和方法

在这里插入图片描述

抽象:

abstract可以修饰类,方法。

abstract修饰类:抽象类

  • 此类不能被实例化
  • 抽象类中一定有构造方法,以便子类去实例化,默认不写

abstract修饰方法:抽象方法:

  • 抽象方法只是方法的声明,没有方法体
  • 包含抽象方法的类,一定是抽象类,抽象类可以不包含抽象方法
  • 若子类重写了父类的所有抽象方法,此子类可实例化
  • 若子类没有重写父类的抽象方法,则子类也是抽象类,使用abstract修饰

abstract不能用来修饰:属性,构造器,私有方法,静态方法,final方法,final类

接口:

接口使用interface定义,接口只能继承接口,可以多继承接口

类和接口是并列的两个结构

如何定义接口:

7之前默认不写是public static final 和public abstract

8默认不写是public static final 和public,因为还乐意为static 和 default

  • JDK7及以前:只能定义抽象方法,和全局常量
    • 全局常量:public static final的,但是书写时,可以省略不写
    • 抽象方法:public abstract的,可以省略
  • JDK8,除了全局常量和抽象方法。还可以定义静态方法、默认方法(default)
    • 接口中定义的静态方法只能通过类.方法去调用
    • 通过实现类的对象去调用默认方法
    • 如果子类(实现类)继承的父类和接口中有同名同参数的方法,子类在没有重写的前提下,会调用父类的方法
    • 如果实现类实现了多个接口,且接口中有同名同参数的方法,程序报错,必须重写实现类中的方法
    • public default void a(){} 正确的
    • public void a(){ } 错误的

接口中不能定义构造器,意味着接口不能被实例化 ----------------- 抽象类中一定有构造方法

Java开发中,接口通过让类去实现(implements)的方式来使用

  • 如果实现类实现了接口的所有的方法,则此类可以被实例化
  • 没有实现接口的所有方法,则还是抽象类
  • 接口可以多继承接口

接口的具体使用,体现多态性

接口,实际上可以看做一种规范

继承可以不重写父类的方法,但是实现就必须重写所有的抽象方法

class Man extends Person implements Cookie,Work{

}

单例模式

所谓单例设计模式,就是采取一定的方法保证在整个软件系统中,对某个类只能存在一个对象实例。

饿汉式 坏处:对象加载时间过长 好处:线程安全

懒汉式 坏处:线程不安全 好处:延迟对象的创建

饿汉式

public class Main {

    public static void main(String[] args) {
        //用类.属性去调用
        Person p = Person.getInstance();
        Person p2 = Person.getInstance();
        System.out.println(p == p2);
    }
}
class Person {
    //1.私有化构造方法
    private Person(){
    }
    //内部实例化一个类,因为要用类.属性调用,所以static
    private static Person instance = new Person();

    //提供get方法去调用这个方法
    public static Person getInstance() {
        return instance;
    }
}

懒汉式

public class Main {

    public static void main(String[] args) {
        //用类.属性去调用
        Person p = Person.getInstance();
        Person p2 = Person.getInstance();
        System.out.println(p == p2);
    }
}
class Person {
    //1.私有化构造方法,为了不让外部去new对象
    private Person(){
    }
    //属性设置为null,因为要用类.属性调用,所以static
    private static Person instance = null;

    //提供get方法去调用这个方法
    public static Person getInstance() {
        //判断调用getInstance()方法时,得到的是不是同一个
        if (instance == null){  //==null就说明之前没创建,可以实例化一个
            instance = new Person();
        }
        return instance;  //说明之前new过
    }
}
public class singleTest {
    public static void main(String[] args) {
        Bank b = Bank.getB();
        Bank b1 = Bank.getB();
        System.out.println(b == b1);
    }
}
class Bank{
    private Bank(){

    }
    private static Bank b = null;

    public static  Bank getB(){  
      
        if (b == null) {
            synchronized (Bank.class) {
                if (b == null) {
                    b = new Bank();
                }
            }
        } 
        return b;
    }
}

异常:(Throwable)

Error:
  • java虚拟机无法解决的问题。
Exception:
  • 编译时异常

    • IOException
      • FileNotFoundException
  • 运行时异常

    • NullPointerException

    • int a[][] = new int[3][];
      a[0][1]
      
    • ArrayIndexOutOfBoundsException

    • int a[] = new int[3]
        a[3]
      
    • ClassCastException (类不一样)

    • Object obj = new Date();
      String str = (Obj)obj
      
    • NumberFormatException(数值转换)

    • String a = "abc";
      Integer b = Integet.parseInt(a);
      
    • InputMismatchException(输入不匹配,输入数字,却输入字符)

    • Scanner sc = new Scanner(System.in);
      int score = sc.nextInt();
      sout
      
    • ArithmeticException

    • int a = 0;
      int b = 1;
      b/a;
      

异常的两种处理方式

try{ }catch(异常类型1 异常变量1){ }finally{ }

  • finally是可选的

  • 使用try将可能出现异常的代码包装起来,在执行过程中一旦出现异常,就会生成一个对应异常类的对象,根据此对象的类型,去catch去匹配

  • 一旦try中异常对象和catch()中一致,就进入catch进行处理,处理完成,跳出当前try -catch结构,在没有写finally的情况下。继续执行之后的代码

  • catch中的异常如果没有子父类关系,和顺序无关,如果有关系,子类应定义在父类前边,否则,报错

  • 常用的异常对象处理方式①String getMessage()②printStackTrace()

  • 在try中定义的出了try就不能被调用,在外部初始化赋值

  • finally表示这一段代码必须被执行,就像资源的关闭,就是说jvm垃圾处理机制不执行的代码,

throws + 异常类型

  • throws + 异常类型 写在方法的声明处,指明可能抛出的异常
  • 一旦方法执行后,出现异常,判断是否和throws后的异常类型一致,一致就抛出,
  • 异常后续的代码不再执行
  • throws方式,把异常交给了调用者,调用者还要去处理

手动异常throw

throw一个异常对象,并抛出,这个异常之后的代码就不执行了,

throw new Exception();

如何自定义异常类:

  • 创建一个类去继承RuntimeException或Exception类
  • 提供全局变量:serialVersionUiD
  • 提供重载的构造器

throw和throws:

  • 一个是手动生成异常并抛出,一个是处理异常的方式之一
  • throw在方法体内,throws在方法后

线程:

在这里插入图片描述

第一种:

存在线程安全问题

  • 创建一个类去继承Thread类。
  • 重写run()方法,在run()方法中写出程序,不能直接调用run()方法,要不然就是单线程,怎么证明?Thread.currentThread().getName()
  • 在主类中创建这个类的实例
  • 调用start()方法①启动当前线程②调用当前线程的run()
  • 一个对象只能调用一次start()方法,会报错,需要重新创建一个线程对象
public class Main{
    public static void main(String[] args) {
        //3.创建子类对象
        Person person = new Person();
        //4.调用start()
        person.start();
        //person.run();
        for (int i = 0; i < 100; i++) {
            if (i%2==0){
                System.out.println(Thread.currentThread().getName()+":"+i);
            }
        }
    }
}
//1.创建一个类去继承Tread类
class Person extends Thread{
    //2.重写run()
    @Override
    public void run(){
        for (int i = 0; i < 100; i++) {
            if (i%2==0){
                System.out.println(Thread.currentThread().getName()+":"+i);
            }
        }
    }
}

Thread中常用方法:

  • start(),启动当前线程;调用当前线程的run()方法
  • run(),子类需要重写Thread类的此方法,将要操作的代码写在其中
  • currentThread(),静态方法,返回当前线程
  • getName(),获取当前线程的名字
  • setName(),设置当前线程名字
  • yield(),静态方法,释放当前cpu的执行权
  • join(),在线程a中调用线程b的join(),此时线程a进入阻塞状态,直到线程b完全执行完以后,线程a才结束阻塞状态。
  • sleep(毫秒),执行到这时,阻塞多少毫秒,
  • isAlive(),判断当前线程是否存活

线程的优先级:

  • MAX_PRIORITY : 10
  • MIN_PRIORITY:1
  • NORM_PRIORITY:5 ---->默认都是5
  • getPriority():获取当前线程的优先级
  • setPriority():设置线程优先级
  • 高优先级会抢占低优先级的cpu执行权,只是从概率上讲可能大一点,并不一定会真的抢占。

第二种:

存在线程安全问题

  • 创建一个类去实现Runable接口
  • 实现run()方法
  • 在主类中创建子类对象
  • 在主类中创建Thread类的对象,并将子类对象放入其中
  • 调用Thread的start()方法
public class Main{
    public static void main(String[] args) throws InterruptedException {
        Windows windows = new Windows();

        Thread thread = new Thread(windows);
        Thread thread1 = new Thread(windows);
        Thread thread2 = new Thread(windows);

        thread.setName("窗口1");
        thread1.setName("窗口2");
        thread2.setName("窗口3");

        thread.start();
        thread1.start();
        thread2.start();

    }
}

//1.创建一个类去实现Runable接口
class Windows implements Runnable{
//2.实现run()方法
    private int ticket = 100;
    @Override
    public void run() {
        while (true){
            if (ticket<0)
                break;
            System.out.println(Thread.currentThread().getName()+":"+ticket);
            ticket--;
        }
    }
}
两种方式对比:
  • 实现的方式比较好,因为继承只能单继承,实现可以实现多个接口

  • 实现可以处理多个线程共享数据的问题

  • Thread类也实现了Runable接口

程序:一段静态的代码

进程:正在运行的程序

线程:进程的分支

Java中,我们通过同步机制,来解决线程安全问题

同步代码块:
  • synchronized(同步监视器、锁){ //需要被同步的代码 } 就是将操作共享数据的代码包起来,
  • 锁,就是任何类的对象,类也是对象。锁必须保持唯一性,不能创建多个对象
  • 在实现Runable接口的方式中,可以使用this作为锁
  • 在继承Thread类的方式中,必须保持创建对象的唯一性,要加static,不能使用this,可以考虑使用当前类去充当
public class Window {
    public static void main(String[] args) {
        W w = new W();
        W w1 = new W();
        W w2 = new W();

        w.start();
        w1.start();
        w2.start();
    }
}
class W extends Thread{
    private static int  ticket = 100;
    static Object o = new Object();    //因为这个是new了三个对象,所以new了三个o,一定要保证锁的唯一
    @Override
    public void run() {
        while (true) {
            synchronized (o) {
                if (ticket <= 0)
                    break;
                System.out.println(currentThread().getName() + ":" + ticket);
                ticket--;
            }
        }
    }
}
同步方法:
  • 创建一个方法,把要同步的代码放入其中,并使用synchronized修饰
  • 同步方法仍然涉及到同步监视器,只是不需要显示声明
  • 在实现Runable接口中,默认的是this,非静态是this
  • 在继承Thread类中,默认的是当前类本身,静态是当前类本身
Lock锁:
  • 在类中实例化ReentrantLock,使用try{ lock.lock }finally{ lock.unlock }
public class Windows3 {
    public static void main(String[] args) {
        Window7 window7 = new Window7();

        Thread thread = new Thread(window7);
        Thread thread1 = new Thread(window7);
        Thread thread2 = new Thread(window7);

        thread.start();
        thread1.start();
        thread2.start();
    }
}
class Window7 implements Runnable{

    private int ticket = 100;
    private ReentrantLock reentrantLock = new ReentrantLock();
    @Override
    public void run() {

        while (true) {
            try {

                reentrantLock.lock();
                if (ticket > 0) {
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + ":" + ticket);
                    ticket--;
                } else {
                    break;
                }
            }finally {
                reentrantLock.unlock();
            }

        }
    }
}
synchronized和ReentrantLock的区别:

相同:都可以解决同步问题

不同:synchronized机制在执行完相应的同步代码以后,自动释放同步监视器

​ ReentrantLock需要手动的启动同步(lock()),同步结束也需要手动关闭(unlock());

在这里插入图片描述

/**
 * @author ssp
 * @date 2021年07月15日 21:54
 *银行有一账户,
 * 有两个储户分别向同一个账户存3000元,每次存1000,存3次,每次存完打印账户余额
 *
 */
public class BankTest {

    public static void main(String[] args) {
        Account account = new Account();

        Person p = new Person(account);
        Person p1 = new Person(account);
        p.setName("甲");
        p1.setName("乙");
        p.start();
        p1.start();
    }
}
class Account{

    private double balance = 0;
    private static ReentrantLock lock = new ReentrantLock();//设置static为了共享锁
    
    public void show(double a){
        try {
            lock.lock();
            if (balance>=0){
                balance += a ;
                Thread.sleep(1000);
                System.out.println(Thread.currentThread().getName()+"存入成功,余额为:"+balance);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }

    }
}
class Person extends Thread{

    private Account account;  //为了获取Account对象,然后调用构造方法时,直接传过去
    public Person(Account account){
        this.account = account;
    }

    @Override
    public void run() {
        for (int i = 0; i < 3; i++) {
            account.show(1000);
        }
    }
}
线程通信三个方法:

wait():一旦执行到该方法,当前线程就会阻塞,释放锁

notify():一旦执行此方法就会唤醒被wait()的一个线程,如果有多个就会唤醒高线程

notifyAll():一旦执行此方法就会唤醒所有被wait()的线程

三个方法都得在同步代码块或同步方法中

都定义在Object中

sleep()和wait()方法:
  • 相同点:都使当前线程进入阻塞状态
  • 不同点:sleep()定义在Thread中,wait()定义在Object中
  • sleep()可以在任何场景调用,wait()只能在同步代码块和同步方法中
  • 如果都在同步代码块或同步锁中,sleep()不会释放锁,wait()会释放锁

第三种:

  • 定义一个类去实现Callable接口。
  • 重写call()方法。
  • 实例化这个子类
  • 实例化FutureTask,将子类放入其中
  • 创建一个线程,将FutureTask的实例化放入其中,
  • 调用start()方法
  • 使用FutureTask的get方法得到call()方法的值,使用Object类型接收
//①定义一个类去实现Callable接口
class NumberThread implements Callable {

    int sum = 0;
    //②重写call()方法
    @Override
    public synchronized Object call() throws Exception {
        for (int i = 0; i <= 100; i++) {
            if (i % 2 == 0){
                sum += i;
                Thread.sleep(100);
                System.out.println(Thread.currentThread().getName()+":"+i);
            }
        }
        return sum;
    }
}
public class NumberTest {

    public static void main(String[] args) {
        //③实例化这个类
        NumberThread thread = new NumberThread();
        //④实例化FutureTask类,将子类的对象放入其中
        FutureTask task = new FutureTask(thread);
        //⑤创建一个新的线程去放入FutureTask的实例化对象,调用start()方法
        Thread t1 = new Thread(task);
        t1.start();
        try {
            //⑥调用FutureTask的get方法的到子类的输出,用Object接受
            Object a = task.get();
            System.out.println(a);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
}

如何理解实现Callable接口比实现Runable接口更好

  • call()方法有返回值
  • call()方法可以抛出异常,被外面捕获,获取异常信息
  • Callable支持泛型

第四种:

使用线程池好处

1.提高响应速度(减少了创建新线程的时间)

2.降低资源消耗(重复利用线程池中线程,不需要每次创建)

3.便于线程管理

  • 提供指定数量的线程池

  • ExecutorService service = Executors.newFixedThreadPool(10);
    
class Run implements Runnable{

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println(i);
        }
    }
}
public class PoolTest {

    public static void main(String[] args) {

        //提供指定线程数量的线程池
        ExecutorService service = Executors.newFixedThreadPool(10);
        //执行指定线程的操作,需要提供实现Runable接口和Callable接口的对象
        //service.execute(new Run());//适合Runable
        service.submit(new Run());//适合Runable和Callable

        service.shutdown();//关闭连接池
    }
}

String:

String,StringBuffer,StringBuild三者异同?

String是不可变序列,底层使用char[]

StringBuffer是可变序列:线程安全,效率低,底层使用char[]

StringBuild是可变序列,线程不安全,效率高,底层使用char[]

String–>StringBuffer,StringBuild :调用构造器

StringBuffer stringBuffer = new StringBuffer("abc");
String s = new String(stringBuffer);

StringBuffer,StringBuild–>String:调用构造器,调用·toString()

String s1 = "abc";
StringBuffer stringBuffer1 = new StringBuffer(s1);
  • String声明为final的,不可被继承
  • String实现了Serializable接口:表示可序列化的,实现了Comparable接口,表示可以比较大小
  • String内部定义了final char[] value用于存储字符串数据
  • String s = “abc”,这种方式定义一个字符串存储在常量池中,常量池中是不会存储两个相同的字符串
    • 重新赋值后,会重新生成新的地址
    • 对现有的字符串进行连接操作时,也会生成新的地址
    • 当调用replace()方法进行字符串修改时,也需要重新指定内存地址
				String s = "abc";
        String s1 = "123";

        String s2 = "abc123";
        String s3 = "abc"+"123";
        String s4 = s + "123";
        String s5 = "abc" + s1;
        String s6 = s4.intern();

        System.out.println(s2 == s3); //true
        System.out.println(s2 == s4);//false
        System.out.println(s3 == s4);//false
        System.out.println(s2 == s5);//false
        System.out.println(s2 == s6);//true

如果是直接赋值的字符串和拼接的字符串,他们的数据相同

  • 常量与常量的拼接结果在常量池,且常量池终不会存在相同的常量

  • 只要其中有一个变量,结果就在堆中

  • 如果拼接的结果调用intern()方法,返回值就在常量池中

length():返回字符串的长度

charAt(int index):返回指定位置的字符

isEmpty():判断是否为空串

toLowerCase():将String中所有字符转换为小写

toUpperCase():将String中所有字符转换为大写

trim():去除字符串首尾的一个或多个字符串

equals():比较两个字符串的内容是否相同

equalsIgnoreCase():忽略大小写比较两个字符串的内容是否相同

concat():将指定字符串连接到此字符串结尾,相当于“+”

int compareTo(String anotherString):比较两个字符串的大小

  • 如果内容的长度相等,比较的是大小。

String subString(int beginindex):获取从index开始朝后的字符串

String subString(int beginindex,int endindex):获取从beginindex开始到endindex之间的字符串(前报后不包)

endWith():判断是否以指定字符串结尾

startWith():判断是否以指定字符串开始

indexOf():返回指定字符串第一次出现的索引

valueOf():将包装类转换为String

replace(char old,char new):返回一个被new替换的新的字符串

split(),按指定字符切分

String -->char[] :调用String的toCharArray()方法

char[]–>String,调用String的构造器

StringBuffer:

  • 可变的字符序列,线程安全,但是执行效率低

    String s = new String();  //char[] value = new char[0];
            String s1 = new String("abc");//char[] value = new char[]{'a','b','c'}
    
            StringBuffer sb1 = new StringBuffer(); //char[] value = new char[16];底层创建了一个长度为16的字符集合
            System.out.println(sb1.length());   //0
            sb1.append('a');  //value[0] = 'a';
            sb1.append('b');  //value[1] = 'b';
            System.out.println(sb1);            //ab
    
            StringBuffer sb2 = new StringBuffer("abc");//char[] value = new char["abc".length() + 16]
            System.out.println(sb2.length());   //3
    

    提前就会空出来16位

    如果要添加的数组底层数组盛不下,就扩容数组

    默认情况,扩容为原来容量的两倍+2,同时将原有数组中的元素复制到新的数组中

append():添加字符串

delete(int start,int end):删除指定位置的字符

replace(int start,int end,String str):更换指定位置的字符

insert(int offset,XXX),在指定位置插入XXX,不是替换

reverse():反转字符串

Date:

一个在util下,一个在sql下

System.currentTimeMills():获取从1970.1.1日到今天的毫秒数

1.两个构造器

2.两个方法

  • toString():显示当前日期
  • getTime():获取毫秒数
				Date date1 = new Date();
        System.out.println(date1.toString());//当前日期
        
        System.out.println(System.currentTimeMillis()); //当前毫秒数
        System.out.println(date1);      //当前日期
        System.out.println(date1.getTime());   //当前毫秒数
        
        Date date = new Date(1626432584889l);  //创建指定毫秒数的日期
        System.out.println(date);  

SimpleDateFormate:

//定义一个自定义格式的年月日
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
        //获取时间
        Date date = new Date();
        System.out.println(date);
        //将时间转换成一个字符串
        String s = simpleDateFormat.format(date);
        System.out.println(s);
        //定义一个时间格式,要求这个字符串必须是和定义的格式一致
        String s1 = "2021-08-17 02:50:36";
        //将这个时间格式转换成默认的
        Date date1 = simpleDateFormat.parse(s1);
        System.out.println(date1);

将java.util.Date转换为java.sql.Date

String s = "2021-06-16";
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
        //这个是java.util.Date
        Date date = dateFormat.parse(s);
        System.out.println(date);
        //调用构造器转换,date.getTime()
        java.util.Date date1 = new java.sql.Date(date.getTime());
        System.out.println(date1);

Calendar日历类是个抽象类

**Calendar.getInstance();**调用

LocalDate,LocalTime,LocalDateTime:

都是抽象类

实例化:调用静态方法。now()和of()

LocalDate localDate = LocalDate.now();
        LocalTime localTime = LocalTime.now();
        LocalDateTime localDateTime = LocalDateTime.now();
        System.out.println(localDate);
        System.out.println(localTime);
        System.out.println(localDateTime);

        LocalDateTime localDateTime1 = LocalDateTime.of(2021,10,22,4,50,30);
        System.out.println(localDateTime1);

Instant:瞬时

now()方法去实例

Comparable:

Comparable接口的使用:(自然排序)

  • 因为String、包装类等实现了Comparable接口,重写了compareTo(obj)方法,所以定义为String和包装类的对象数组能比较
  • 对于自定义列来说,如果需要排序就实现Comparable接口,重写CompareTo()方法,指明如何排序。

Comparator:

  • 实现compare(object a,object b)方法比较

区别:Comparable接口的方式一旦确定,保证Comparable接口实现类对象在任何位置比较大小都可以。Comparator接口,属于临时性比较。

枚举类:

类的对象只有有限个,确定的。比如:季节,只有春夏秋冬。星期:(1-7)

当需要定义一组常量时,强烈建议使用枚举。若枚举类只有一个对象,则可以作为单例模式实现方式

  • 在5.0之前,要自定义枚举类

  • 可以使用enum关键字来定义

    • public class enumTest {
          public static void main(String[] args) {
            //直接使用类。对象,默认使用public static final
              Season season = Season.SPRING;
              //定义的枚举类默认继承与java,lang。Enum类
              System.out.println(season);//SPRING
          }
      }
      enum Season{
      
          //1.首先提供当前枚举类的对象,用“,”隔开,最后使用“;”
          SPRING("春天","春天来了"),
          SUMMER("夏天","夏天来了");
      
          //2.声明Season的属性
          private final String seasonName;
          private final String seasonDesc;
          //3.私有化构造器,并给属性赋值
          private Season(String seasonName,String seasonDesc){
              this.seasonName = seasonName;
              this.seasonDesc = seasonDesc;
          }
          public String getSeasonName(){
              return seasonName;
          }
          public String getSeasonDesc(){
              return seasonDesc;
          }
      }
      
    • toString():返回当前对象的名称

    • values():返回当前枚举类的所有对象

    • valueOf(String objName):返回对象名是objName的对象

集合:

集合,数组都是对多个数据进行存储操作的结构,简称Java容器

数组在存储数据的特点:

  • 一旦初始化,长度就确定了。
  • 数组一旦定义好,其元素的类型也就确定了,我们只能操作指定类型的数据,可以存储基本数据类型和对象,集合只能存储对象类型

数组在存储数据的缺点:

  • 一旦初始化长度就不可以改变
  • 数组提供的方法非常有限,对于增删改查等操作,不方便,效率不高
  • 获取数组中实际元素的个数,数组没有方法去获得
  • 数组存储数据的特点:有序,可重复。
Java集合可分为Collection和Map体系:
  • Collection接口:单列数据
    • List:元素有序,可重复
    • set:元素无序,不可重复
  • Map接口:双列集合,key - value对集合
Collection接口的方法:
add(object)添加元素
addAll(Collection c) 将其他集合中的元素,添加到其中
size()方法 查看有多少个元素
toString() 输出集合元素
remove() 删除指定名称的元素
removeAll():删除集合1中所有和集合2相同的数据
clear()  清空集合元素
isEmpty() 判断集合元素是否为空
contains(Object o): 内部使用equals()进行判断,因为String重写了,所以是值对比,如果是自定义对象,且没有重写equals方法,比较的就是地址
containsAll(Collection c):判断集合1中是否包含集合2
retainAll(); 求集合1和集合2的交集
equals():比较两个集合是否相同
toArray():集合转化为数组
Iterator:集合遍历

hasNext():判断是否有下一个元素,从第一个元素的前边开始,

next():指针下移,将下移以后的结合位置上元素返回

Iterator iterator = coll.iterator();

        while (iterator.hasNext()){
            Object o = iterator.next();
            if ("123".equals(iterator)){
                iterator.remove();
            }
            System.out.println(iterator.next());
        }

遍历一遍之后,不能再用同一个iterator去遍历,因为这是指针已经在末尾了,需要重新获得一个迭代器对象

增强for循环:

遍历对象元素,内部使用迭代器

for(Object o : 对象){

}

ArrayList,LinkedList,Vector的区别:

都是实现了List接口,存储数据特点相同:有序的,可重复的

  • ArrayList:作为List接口的主要实现类,线程不安全,效率高,对于查询操作,效率高,底层使用数组存储
  • LinkedList:对于频繁的插入,删除操作,使用次类效率高,底层使用双向链表
  • Vector:线程安全,效率低,底层使用数组存储

ArrayList源码分析:

​ jdk7:

  • ArrayList list = new ArrayList()//底层创建了长度为10的object[]数组。

  • 如果add()超过第10个,数组就会进行扩容,默认情况下,扩容为原来的1.5倍,同时将原本数组copyof()到新数组中

  • ArrayList list = new ArrayList(int capacity),使用带参的构造器创建指定长度的数组

    jdk8:

  • ArrayList list = new ArrayList(), //底层创建了的object[]数组,初始化为{},并没有创建长度。

  • add(),第一次add()操作时,底层才创建了一个为10的数组

LinkedList:

  • LinkedList list = newLinkedList(),内部声明了Node类型的first和last,属性默认是null,
  • add(),将add的属性封装到Node中,创建了Node对象

Vector:

  • 扩容为两倍
List常用方法:
  • add(int index,Object o):在指定位置插入元素

  • addAll(int index,Collection c):在指定位置插入集合

  • get(int index):得到某个位置的元素

  • indexof(Object o):返回指定元素在第一个出现的索引

  • lastIndexOf():返回指定元素在最后一个出现的索引

  • remove(int index ):删除指定位置的元素

  • set(int index,Object o):设置指定index位置的元素

  • List subList(int formIndex,int toIndex):返回两个区间之内的元素

Set:

HashSet:作为Set接口的主实现类,线程不安全,可以为null,底层数组+链表,初始容量为16,扩容为2倍

**LinkedHashSet:**作为HashSet的子类;遍历内部数据时,按照添加的顺序遍历

**TreeSet:**可以按照添加对象的指定属性,进行排序。

**无序:**不等于随机性。存储的数据在数组中按照哈希值去排序

**不可重复性:**保证添加的元素按照equals()判断时,不能反悔true,即相同的元素只能添加一个,根据hashCode()去判别

添加元素的过程:以HashSet为例:

我们向HashSet中添加元素a,首先调用元素a的hashCode()方法,计算元素a的哈希值,次哈希值通过某种算法计算出在HashSet底层数组中存放的位置,如果此位置上没有值,则添加成功,如果有元素b(或以链表形式存在的多个元素),则比较元素a和元素b的哈希值,如果不相同,则成功,如果相同,则需要调用a的equals()方法,如果返回值为true则不成功,为false则成功。如果这个位置有元素,则就以链表的形式存储。

jdk7:新添加的元素在数组中,指向原来的元素

jdk8:原来的元素在数组中。指向元素a

向Set中添加数据,其所在的类一定要重写hashCode()和equals()

TreeSet中添加数据时,必须是同种类型的。可以进行排序,排序方法为自然排序和定制排序,红黑树

自然排序中,比较两个对象是否相同的标准为compareTo()返回值,不在是equals()
在这里插入图片描述

Map:
  • HashMap:作为Map的主实现类,线程不安全,效率高,可以对key和value存储null
    • LInkHashMap:保证在遍历Map元素时,可以按照添加的顺序实现遍历,在原有的HashMap底层中,添加了指针,对于频繁的遍历操作,执行效率高于HashMap
  • TreeMap:保证按照添加key-value对进行排序,实现排序遍历,此时考虑key的自然排序和定制排序,底层使用红黑树
  • Hashtable:作为1.0的实现类,线程安全,效率低,不能对key和value存储null
    • Properties:常用来处理配置文件。key和value都是String类型

Map结构:

  • Map中的Key:无序的,不可重复的,使用Set存储所有的Key,key所在类必须要重写HashCode和equals方法。
  • Map中的Value:无序的,可重复的,使用Collection存储所有的value,value所在类要重写equals()方法
  • 一个键值对:key-value构成了一个Entry对象
  • Map中到的entry:无序的,不可重复的,使用Set存储所有的Entry

HashMap底层实现原理:

jdk7:

HashMap map = new HashMap():调用空参构造器,底层创建了长度为16的一维数组Entry[] table。

map.put(key,value)

首先调用key所在类的HashCode()计算key的哈希值,此哈希值经过某种算法后,得到在Entry的存放位置。如果此位置没有元素,则存放成功。如果有元素(意味着存在一个或多个数据(以链表的形式存储)),比较key和已经存在的数据的哈希值,如果哈希值不相同,则存放成功,如果和此位置上的任意一个相同,则比较equals()方法,返回true,则使用value替换已经存在的value,返回false,则添加成功。

默认扩容为原来的2倍,当超过临界值(且该数据存放的位置为空)并将原有数据copyof过来

jdk8相较于jdk7的不同:

  • new HashMap():底层没有创建一个长度为16的数组
  • 8底层使用的Node[] ,而非Entry[]
  • 首次调用put方法时,生成16位的数组
  • jdk7底层结构只有数组和链表 ,8中有数组+链表+红黑树
  • 当数组的某一个索引位置上的元素以链表存在的数据个数 > 8且当前数组的1长度 > 64时,此时该索引位置使用红黑树存储。
  • jdk7的时候,新的元素指向旧的元素,8,旧的元素指向新的元素

默认容量:16

default_LoAD_FACTOR:默认加载因子:0.75。决定了HashMap的密度

扩容的临界值:容量 * 扩容因子 = 16 * 0.75 =12

map.keySet():返回Set集合的Key,使用iterator遍历

map.values(): 返回value,

map.entrySet():返回key-value值,

TreeMap:

向TreeMap中添加key-value,要求Key必须有同一个类创建的对象。自然排序和定制排序

HashMap map = new HashMap();
        map.put("小黄",123);
        map.put("小蓝",12);
        map.put("小红",13);
        map.put("小绿",23);
        System.out.println(map);
//        Set set = map.entrySet();
//        Iterator iterator = set.iterator();
//        while (iterator.hasNext()){
//            Object next = iterator.next();
//            Map.Entry entry = (Map.Entry) next;
//            System.out.println(entry.getKey()+"-----"+entry.getValue());
//        }
        Set set = map.keySet();
        Iterator iterator = set.iterator();
        while(iterator.hasNext()){
            Object next = iterator.next();
            Object o = map.get(next);
            System.out.println(next+"---"+o);
        }

ArrayList和HashMap都是线程不安全的,如果想要安全,使用Collections中的synchronizedList(List list)和synchronizedMap(Map map)

泛型:

要保证里边输出的数据,必须是同一种类型的。

在实例化集合类时,可以指明具体的泛型类型

在指明泛型以后,内部结构凡是使用到泛型,都必须声明为指定的类型

比如add(E e)—>add(Integer i)

泛型必须是类,不能是基本数据类型,如果是也是包装类,如果没有用泛型,默认Obejct

public class CollectionTest {
    public static void main(String[] args) {
        //指定为Integer类型,只能放Integer类型的值,泛型中不能使用基本数据类型
        ArrayList<Integer> list = new ArrayList<Integer>();
        list.add(123);
        list.add(456);
//        list.add("123");//报错
        Iterator<Integer> iterator = list.iterator();
        while (iterator.hasNext()){
            Integer integer = iterator.next();
            System.out.println(integer);
        }

        HashMap<String,Integer> map = new HashMap<String,Integer>();
        map.put("小黄",123);
        map.put("小黄",456);
        map.put("小红",123);
        //Entry为Map的内部接口,Set类型,里边是Entry值
        Set<Map.Entry<String,Integer>> set = map.entrySet();
        Iterator<Map.Entry<String,Integer>> iterator1 = set.iterator();
        while (iterator1.hasNext()){
            //Entry类型
            Map.Entry<String,Integer> entry = iterator1.next();
            String key = entry.getKey();
            Integer value = entry.getValue();
            System.out.println(key+"----"+value);
        }
    }

自定义泛型:

在定义的类后边加上,在类中声明T person;

T person; //可以定义一个泛型的变量

如果new的对象没有使用泛型,则默认Object

如果限制了类型,则只能填入正确类型

子类继承了父类的泛型,如果为,则子类想要使用某一类型时,需自己声明

如果父类指明了类型,则生成的子类中继承了父类中的泛型,可以不写

File:

File类的对象代表一个文件或一个文件目录

File类声明在java.io

File类涉及到文件或文件夹的创建(mkdir,mkdirs,createNewFile),删除(delete),重命名(renameTo),长度(length()),存在(exists),

并未涉及到写入或读取文件的操作,那需要使用IO流

IO:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Tlw7LcBI-1627206416799)(Java基础.assets/1626950505681.png)]

流的分类:

  • 按数据流向:输入流,输出流
  • 操作数据的单位:字节流,字符流
  • 流的角色:节点流,处理流

对于文本文件(.txt.java.c.cpp),使用字符流,对于文本文件,只是复制操作时,不查看,可以使用字符流。

对于非文本文件(。jpg。avi。mp3,。doc),使用字符流

  • 创建文件
  • 创建节点流
  • 操作流
  • 关闭流

抽象基类 节点流(文件流) 缓冲流(处理流)

InputStream FileInputStream BufferInputStream

OutputStream FileOutputStream BufferOutputStream

Reader FileReader BufferReader

Writer FileWriter BufferWriter

File file = new File("src/main/java/org/day5/hello.txt");

        FileReader fileReader = null;

        try {
            //1.创建FileReader对象
            fileReader = new FileReader(file);
            while (true){
                //2.读取一个字符,不带参方法返回int类型,-1为最后
                int data = fileReader.read();
                if (data!=-1) {
                    //需要强转一下,否则就输出该字符的ASCLL码
                    System.out.print((char) data);
                }else
                    break;
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                //一定记得关闭流
                fileReader.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

read():读取一个字符,不带参方法返回int类型,-1为最后

异常处理:使用try-catch-finally,因为要保证finally的执行

read(char[]): 读取指定位数的字符,返回读取数字

ileReader fileReader = null;
            try {
                //1.声明文件名称
                File file = new File("src/main/java/org/day5/hello.txt");
                //2.创建对象
                fileReader = new FileReader(file);
                int length = 0;
                char[] c = new char[5];
                //方式1:正确的
//                while ((length = fileReader.read(c) )!= -1){
//                    for (int i=0; i<length; i++){
//                        System.out.print(c[i]);
//                    }
//                }
                while ((length = fileReader.read(c)) != - 1) {
                    String string = new String(c, 0, length);
                    System.out.print(string);
                }
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    fileReader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

输出操作,对应的File可以不存在,系统会自动创建文件

如果存在,流使用FileWriter(file,false)或FileWriter(file),则会覆盖原本文件的内容。如果使用FileWriter(file,true),则不会覆盖原有文件内容

/**
         * 从hello中读取文件存到hello2中
         */
        FileReader fr = null;
        FileWriter fw = null;
        try {
            File srcfile = new File("src/main/java/org/day5/hello.txt");
            File desfile = new File("src/main/java/org/day5/hello2.txt");

            fr = new FileReader(srcfile);
            fw = new FileWriter(desfile);

            char[] c = new char[5];
            int len;
            while ((len = fr.read(c)) != - 1){
                fw.write(c,0,len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {

            try {
                if (fr!=null)
                fr.close();
            } catch (IOException e) {
                e.printStackTrace();
            } 
            try {
                fw.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
缓冲流:提高流的读取、写入速度。原因:内部提供了一个缓冲区(8 * 1024)
public void Buffer() {
        //1.创建文件
        BufferedInputStream bis = null;
        BufferedOutputStream bos = null;
        try {
            File srcfile = new File("src/main/java/org/day5/珂珂.jpg");
            File destfile = new File("src/main/java/org/day5/珂珂2.jpg");

            //创建节点流
            FileInputStream fis = new FileInputStream(srcfile);
            FileOutputStream fos = new FileOutputStream(destfile);
            //创建缓冲流
            bis = new BufferedInputStream(fis);
            bos = new BufferedOutputStream(fos);
            //定义字节数组接受
            byte[] b = new byte[5];
            int len;  //定义长度去接收读取的位数
            while ((len = bis.read(b)) != -1){
                bos.write(b,0,len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
        //4.关闭外层流,此时内层流也会关闭
            if (bis != null) {
                try {
                    bis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (bos!=null) {
                try {
                    bos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

    }
转换流:

字符类型:使用char[]

InputStreamReader:实现字节输入流到字符输入流的转换,

OutPutStreamWriter:实现字符输出流到字节输出流的转换

网络编程:

1、如何准确的定位网络上的一台或多台主机;定位主机上的特定应用

2、找到主机后如何可靠高效的进行数据传输

网络编程的两个要素:

  • IP和端口号
  • 提供网络协议

OSI:物理层-》数据链路层-》网络层-》传输层-》会话层-》表示层-》应用层

TCP/IP:物理层+数据链路层-》网络层-》传输层-》应用层

通信要素一:IP和端口号

IP:

  • 唯一标识Internet上的计算机
  • 在Java中使用InetAddress类代表IP
  • IP分类,IPv4,IPv6。万维网和局域网(192.168.0.0)
  • 域名。www.baidu.com
  • 本地地址,137.0.0.1,对应着localhost
  • 如何实例化InetAddress:getByName(String host),getLocalhost()
  • 两个常用方法:getHostName(),getHostAddress()

端口号:

  • 正在计算机上运行的进程
  • 要求:不同的进程有不同的端口号
  • 范围:被规定为16位整数,0~65535

端口号与IP地址组合得出一个网络套接字:Socket

TCP协议:

  • 使用TCP前必须先建立TCP连接,形成传输数据通道
  • 传输前,采用“三次握手”方式,点对点通信,可靠的
  • TCP协议的两个应用进程:客户端,服务端
  • 在连接中可进行大数据量的传输
  • 传输完毕,需释放已建立的连接,效率低

UDP:

  • 将数据、源、目的封装成包,不需要建立连接
  • 每个数据包大小限制在64K
  • 发送不管对方是否准备好,接收方收到也不确认,故是不可靠的
  • 广播,直播
  • 发送数据结束时,无序释放资源,开销小,速度快
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值