跟着楠哥学java(javase笔记)

JAVA介绍(4.14)

JDK

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

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

JRE

JRE(Java Runtime Environment JAVA运行环境)

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

class

首先将后缀名为.java的文件编译为.class的可在JVM上运行的文件

cmd使用

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-w3pLCPAG-1635652961140)(assets/cmd使用.png)]

cd :转移路径

-version:查看版本

配置环境变量

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3R2h4hUX-1635652961143)(assets/1618366391307.png)]

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

安装java jdk

可以直接将压缩包解压到C:盘java文件夹中

jshell的使用

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

可以直接在cmd中写一下Java的小程序(一般不用)

JAVA总体命名规则

1、项目名全部小写

2、包名全部小写

3、类名首字母大写,其余组成词首字母依次大写

4、变量名,方法名首字母小写,如果名称由多个单词组成除首字母外的每个单词的首字母都要大写

5、常量名全部大写

所有命名规则必须遵守以下规则:

  • 命名只能由字母、数字、下划线、$符号组成

  • 不能以数字开头

  • 名称不能使用JAVA中的关键字

  • 坚决不允许出现中文及拼音名。

    java语言有51个关键字,其中const和goto虽然被保留但未使用。不能使用保留关键字来命名类、方法、变量。

注释的书写格式

单行注释

//单行注释

多行注释

/*多行注释*/

文档类型注释

/**文档类型注释*/

数据类型

1、数据类型

基本数据类型   byte 1个字节 short 2个字节int  4个字节long  8个字节float   4个字节double  8个字节char  2个字节boolean 一个字节
  • 源码补码反码的取值范围

    正数的源码补码反码都相同

    负数的源码是本身,反码是除符号位其他位取反,补码是反码+1

  • 科学计数法表示浮点数

    • 将十进制变成二进制输出

    System.out.println(“Integer.toBinaryString()”)

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CfhtjiuT-1635652961151)(JAVA 笔记.assets/image-20210414173429389.png)]

    字符编码扩展

    GBK

    GB2312

    UTF-8

    unicode(万国码)

    ISO–8859

2、定义变量

变:能保存一些可变的量,比如年龄,收入等

便: 方便,一次定义,“到处”(作用域内)使用

快:实际上是一个内存地址,c指针,java引用

(1)过程

定义 int i;

赋值 i = 5; 定义和赋值可以一起 int i = 5;

使用;

(2)定义变量的规则

1、变量名字里不能有空格。

2、避免使用关键字,class public int static

3、避免使用汉字

4、整体是驼峰命名,首字母小写

5、$和_可以到处使用

6、数字不能开头

定义在main函数外面的变量为成员变量

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

成员变量:

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

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

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

局部变量:

​ 1、局部变量只定义在局部范围内,如:函数内,语句内等,只在所属的区域有效。

​ 2、局部变量存在于栈内存中,作用的范围结束,变量空间会自动释放。

​ 3、局部变量没有默认初始化值

在使用变量时需要遵循的原则为:就近原则

首先在局部范围找,有就使用;接着在成员位置找。

3、布尔运算-真值表

条件1 condition1 条件2 condition2 结果
1 1 1
1 0 0
0 1 0
0 0 0

条件1 condition1 条件2 condition2 结果
1 1 1
1 0 1
0 1 1
0 0 0

条件1 condition1 结果
1 0
0 1
(1)符号表示

& | ! && ||

&& 短路运算符 如果是false 直接 结果就是false 不会继续运算

|| 短路运算符 如果是true直接 结果就是true 不会继续运算

4、算术运算符

  +  -  *  /   %    ++   --   +=      -=(1+2)*3

5、定义数组

定义

int[]  array = new int[10];

定义及初始化

int[]  array = new int[]{1,2,3};int[]  array = {1,2,3};

6、三目运算符

条件 ? 结果1 : 结果二;

条件的结果一定是boolean ,true ,false;

条件是可以复杂的

boolean condition1 = 5 > 3;boolean condition2 = 5 > 3;boolean condition3 = 5 > 3;int num = (condition1 && (condition2 || condition3)) ? 1 : 2;

7、位移运算符

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vyH9ig3B-1635652961153)(JAVA 笔记.assets/image-20210415202244232.png)]

基础数据类型定义(4.15)

byte b = 10;short s = 100;int i = 1000;long l = 100L;float f = 0.1F;double d = 0.2D;boolean is = true;char c = 'b';

如果有两个不同类型变量之间的加减,自动转成int,或者手动强转

输入输出

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cHHIfGNh-1635652961154)(JAVA 笔记.assets/image-20210415212304745.png)]

循环 判断(4.16)

一、switch用法

String next = scanner.next();int grade  = 1;switch (next){    case "宝军":        grade ++;        break;    case "主宰":        System.out.println("欢迎打主宰。");        grade += 2;        break;    default:        grade += 3;        break;}不能是long型 可以 byte shor int     (不是基础类型)String  

二、循环

1、for

for (int j = 1; j <= 9 ; j++) {    for (int i = 1; i <= j ; i++) {        System.out.print("1 * " + i + " = " + i*1 + "\t");    }    System.out.println();}定义条件变量    进入条件   ; 变量改变    自增为了达到退出条件            

三、while 和 do while

int i = 0;while( i < 5){    ......        i++;}do{    ......    i = i + 8;}while(i < 7)定义 条件变量    进入的条件  变量改变    变量的改变是为了满足退出条件

区别

do while无论如何都要先执行以下,然后才去判断

while 满足条件才能进入

四、几个关键字

break

无论如何都要结束当前的全部循环,程序会继续向下执行。

contiune

跳过本次循环继续下一次循环。

打标签

for循环可已打标签,使用break + 标签名可以 退出 被打标签的循环

flag:for (int i = 0; i < 2; i++) {        for (int j = 0; j < 2; j++) {                if( j > 0 ){                        break flag;               }                System.out.println("===="+j);        }}

数组

一、数组的定义

int[] nums = {1,2,3};int[] nums = new int[3];类型[] 名字 = new 类型[长度];

二、数组的性质

1、数组一旦建立不能改变长度。

2、每个位置只能存一个值,多了会覆盖。

3、编号从0开始,下标。

4、他有个长度的属性,最后一个位置的编号是 长度-1。

5、数组里边可以是基本类型,也可以是引用类型。

三、数组的简单实用

1、打擂台的形式找最大值

int[] salary = {4,5,0,6,7,8};int maxIndex = 0 ;for (int i = 1; i < salary.length; i++) {    if(salary[i] > salary[maxIndex]){        maxIndex = i;    }}System.out.println("经过了n轮比赛得到的最大值得下标是:"+maxIndex+"。值是:"+salary[maxIndex]);

2、通循环遍历打印数组的每一个值

for (int i = 0; i < salary.length; i++) {    System.out.print(salary[i] +  " ");}

3、查找一个数组里存在的值。

int targetIndex = -1;for (int i = 0; i < salary.length; i++) {    if(salary[i] == 9){        targetIndex = i;    }    break;}

4、元素的位移。

int[] salary = {4,5,0,6,7,8};int temp = salary[0];salary[0] = salary[1];salary[1] = temp;

四、排序算法

1、选择

每一轮都是在找一个最小值放在未排好序的元素的第一个

int[] nums = {4,6,0,8,7};for (int j = 0 ; j < nums.length -1 ; j++){    int minIndex = j;    for (int i = j+1; i < nums.length; i++) {        minIndex =  nums[i] < nums[minIndex] ? i : minIndex;    }    int temp = nums[minIndex];    nums[minIndex] = nums[j];    nums[j] = temp;}for (int i = 0; i < nums.length; i++) {    System.out.print(nums[i] + " ");}

	//选择排序		int[] array = {12,4,3,43,56,23,555,14,87,354,65,37,28,54,43,94,56};	//1、找最小值	for (int y = 0; y < array.length - 1; y++){		int targetArray = y;	for (int i = y + 1; i < array.length; i++){		if (array[targetArray] > array[i]){			targetArray = i;		}	}	//2、将最小值与首位交换	int middle = 0;	middle = array[y];	array[y] = array[targetArray];	array[targetArray] = middle;	}	//输出数组每一个值	for (int i = 0 ;i < array.length; i++){		System.out.println(array[i]);	}

2、冒泡

每一轮都是两两相比,会将最大的冒到最后边

int[] nums = {3, 7, 4, 9, 5, 4, 1};for (int j = 0; j < nums.length - 1 ; j++) {    for (int i = 0; i < nums.length - 1 -j ; i++) {        if (nums[i] > nums[i + 1]) {            int temp = nums[i];            nums[i] = nums[i + 1];            nums[i + 1] = temp;        }    }}for (int i = 0; i < nums.length; i++) {    System.out.print(nums[i] + " ");}
//冒泡排序	int[] array = {12,4,3,43,56,23,555,14,87,354,65,37,28,54,43,94,56};	for (int i = 0; i < array.length - 1; i++){		for (int y = 0; y < array.length - i - 1; y++){			if (array[y] > array[y + 1]){				int middle = array[y];				array[y] = array[y + 1];				array[y + 1] = middle;			}		}	}	for(int i = 0; i < array.length; i++){		System.out.println(array[i]);	}

3、插入排序

默认前面的每一个数都是有序的,将后一个数插入到前面有序的数列中去

//插入排序	int[] array = {12,4,3,43,56,23,555,14,87,354,65,37,28,54,43,94,56};	for(int i = 1; i < array.length; i++){		//将要排的数字保存下来		int temp = array[i];		for(int j = i-1; j >= 0; j--){			if (array[j] > temp){				array[j+1] = array[j];				if (j == 0){					array[0] = temp;				}			}			else{				array[j+1] = temp;				break;						}			}	}

IDEA基本设置(4.17)

字体外观

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UILXzRIc-1635652961155)(JAVA 笔记.assets/image-20210417082729788.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pYI0TYop-1635652961157)(JAVA 笔记.assets/image-20210417082846794.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GQQCaa8g-1635652961158)(JAVA 笔记.assets/image-20210417083655453.png)]

1、字体的设置

(1)非代码块设置主题和字体
file --》 settings ---》appearance---》theme  更换主题Use custom font 勾上  选择字体 和 文字大小
file --》 settings --》 plugins --》 installed ---》theme   插件ctrl art shift +s
(2)修改代码块的字体
file --》 setting --》editor --》font

2、配置jdk

(1)启动的时候就能配置

(2)ctrl + alt + shift + s 工程配置

project ---> project Sdk   --> new   找到自己安装的jdk一定要保存  点击ok

3、一些快捷方式

main方法新版的输入 main老板的输入 psvm输出语句   sout复制一行   ctrl+D剪切       ctrl+X注释    ctrl+/自动改错   alt + enterfori shift+F6ctrl+shift+下   和下一行交换位置ctrl+alt+L     格式化代码

增强for循环和一些快捷键

增强for循环

value为一个自变量,每次循环就将数组中的值赋给value然后打印出来,缺点是不能获取下标

int[] arr = {1,2,4,5};for (int value : arr){    System.out.println(value)}

方法(相当于c语言中的函数),方法封装起来叫做对象,描述特征的叫做属性

一些实用方法

1、生成随机数

Math.random()随机生成一个0~1的数字,并且因为是浮点型所以必须转成整形

//概率、随机int a = (int)(Math.random()*10+20);

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uPJfyY4W-1635652961159)(JAVA 笔记.assets/image-20210426094124393.png)]

2、延迟

public static void sleep(int time){    try {        Thread.sleep(time);    }catch (InterruptedException e){        e.printStackTrace();    }

3、数字转字符串

任何东西只要加字符串就是字符串

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sxTlBBwu-1635652961160)(JAVA 笔记.assets/image-20210420154435699.png)]

4、字符串转数字

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-C6xCXDOu-1635652961162)(JAVA 笔记.assets/image-20210420162050517.png)]

5、字符串比较

使用equals

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-y6gIU4SS-1635652961163)(JAVA 笔记.assets/image-20210421161046457.png)]

获取当前时间

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-l9Or9Lii-1635652961163)(JAVA 笔记.assets/image-20210426094936868.png)]

面向对象(4.19)

方法(相当于c语言中的函数),方法封装起来叫做对象,描述特征的叫做属性。

import java.util.Scanner;//包 package ……public class Car {    //定义颜色 可以不赋值    public String color;    public int saddle;    public void run(){        System.out.println("启动");        System.out.println("加油");    }//例子:封装    //方法存在方法区   栈里面存的引用(地址)(执行效率高)、基础数据类型;    public static void main(String[] args) {        /**         *  构造方法         *  1 new 其实是在调用构造方法         *  2 如果一个类里边没有构造方法 会自动创建一个空的构造方法         *  3 构造方法能传参数,在构造期间就把对象得值赋好         *  4 一旦有了新的参数的构造方法,空构造就不在了,如果想保留,就得手动写上         *  Car是类,小car是引用(指向内存里的一片区域)         *  5 重载的构造方法,名字一样,参数不一样。         */        /**         * this 关键字指内存里分配好空间的(对象)         *         * 重载         * 1 方法名相同         * 2 方法的参数类型,参数个数不一样         * 3 方法的返回类型可以不相同         * 4 方法的修饰符可以不相同         * 5 main 方法也可以被重载         *         */        Car car = new Car();        car.color = "red";        car.saddle = 4;        car.run();    }java}

1、包

​ 简单、装类的,就是个文件夹

​ 要求

​ 域名倒着写 com.xinzhi ,必须全部小写 用.隔开。

​ 1、引入其他人写的类的时候保证不重名。

​ 2、一眼就能看出是哪个公司的作品、保护作用。

2、权限修饰符

作用域 当前类 同package 子孙类 其他package
public
protected(受保护的) ×
friendly( default )(默认) ×
private keyi X X

为什么要用

1、对自己而言,保护代码不被污染

2、对别人而言、给别人一个干净的类

关于protected修饰符的访问权限如下:

  • 子类与基类在同一包中:被声明为 protected 的变量、方法和构造器能被同一个包中的任何其他类访问;
  • 子类与基类不在同一包中:那么在子类中,子类实例可以访问其从基类继承而来的 protected 方法,子类不能访问基类实例的protected方法。

4、类 new对象 重点

new对象其实是调用构造方法 ,但我们发现刚写的类里并没有构造方法 ,事实上,如果你的类没有构造方法系统会送一个空的构造方法。

构造方法

构造方法    /***  1 new 其实是在调用构造方法*  2 如果一个类里边没有构造方法 会自动创建一个空的构造方法*  3 构造方法能传参数,在构造期间就把对象得值赋好*  4 一旦有了新的参数的构造方法,空构造就不在了,如果想保留,就得手动写上*  Car是类,小car是引用(指向内存里的一片区域)*  5 重载的构造方法,名字一样,参数不一样。**/

那我们怎么写自己的构造方法

名字和类名完全一样大小写也不能有区别,不能有返回值void也不写。

一旦你定义了自己的构造方法,系统就不会赠送空的构造方法

重载

/**重载* 1 方法名相同* 2 方法的参数类型,参数个数不一样* 3 方法的返回类型可以不相同* 4 方法的修饰符可以不相同* 5 main 方法也可以被重载**/

重点工作、难点一,要理解:

package com.xinzhi.test;/** * @author 张楠 * @date 2020/2/6 */public class Dog {    public static void main(String[] args) {        //使用无参的构造方法        Dog teddy = new Dog();        //使用setter赋值        teddy.setName("金毛");        System.out.println(teddy.getName());        System.out.println("-------------------------------------------------");        //使用有参的构造方法,直接给name赋值        Dog golden  = new Dog("金毛");        System.out.println(golden.getName());        System.out.println("-------------------------------------------------");        //直接new对象看看new的时候是不是调用了构造方法,事实证明是的        new Dog("哈士奇");    }    private String name;    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }    //空的构造方法,名字必须和类名一样,不能有一点区别    public Dog(){    }    //构造方法和其他方法一样可以重载,可以有参数,名字必须和类名一样,不能有一点区别    public Dog(String name){        System.out.println("验证构造方法被调用的时机:【"+ name + "】被创建!");        this.name = name;    }}结果金毛-------------------------------------------------验证被调用的时机:【金毛】被创建!金毛-------------------------------------------------验证被调用的时机:【哈士奇】被创建!

5、setter and getter,规范,记住这么写就行了

所有的属性必须私有化:

使用setter和getter,因为方法能控制取值和赋值的过程。

/** * 定义狗类 * @author zn * @date 2020/2/2 */public class Dog {        //有哪些特点    //定义狗有颜色这个属性    private String color;    //定义狗有种类这个属性    private String type;    //定义狗有年龄这个属性    private int age;        //java约定使用setter和getter方法进行属性的取值和赋值    public String getColor() {        return color;    }    public void setColor(String color) {        this.color = color;    }    public String getType() {        return type;    }    public void setType(String type) {        this.type = type;    }    public int getAge() {        return age;    }    public void setAge(int age) {        this.age = age;    }    //..狗还有很多属性和方法,我们无法一一列举}

6、 封装

extends 单继承,一个爹可以有多个儿子,一个儿子只能有一个爹。

有个 顶级父类叫Object,所有的对象都有一个父类叫Object,这是规定,记住。

所以所有的对象都有object的所有方法。Object obj = new Object();

思想:一个对象调用方法的时候,首先去自己的类里边找,找不到的话就去父类找,父类找也不到,就去父类的父类找,直到Object,找到为止!

重点工作、难点二、一样要理解:

//你建了包会自动给你创建packagepackage com.xinzhi.myextends;/** * 封装一个动物类作为父类 * @author 张楠 * @date 2020/2/6 */public class Animal {    private String name;    public void breathe(){        System.out.println(this.name + "在呼吸。");    }    public void eat(){        System.out.println(this.name + "会吃东西。");    }    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }}
package com.xinzhi.myextends;/** * 封装鸟类 * @author 张楠 * @date 2020/2/6 */public class Bird extends Animal {    //鸟会飞    public void fly(){        System.out.println(this.getName() + "会飞!");    }}
package com.xinzhi.myextends;/** * 封装一个鱼类 * @author 张楠 * @date 2020/2/6 */public class Fish extends Animal {    //鱼类有独立的方法,游泳    public void swimming(){        System.out.println(this.getName() + "会游泳!");    }    //重写呼吸的方法,因为特殊,那么鱼类在调用breathe方法时先来这里找,找到了就不去父类找了    public void breathe() {        System.out.println("我用腮呼吸呦!");    }}
package com.xinzhi.myextends;import java.io.FilterReader;/** * @author 张楠 * @date 2020/2/6 */public class Test {    public static void main(String[] args) {        Bird bird = new Bird();        //bird里没有setName方法,怎么办去父类找        bird.setName("鸟儿");        //bird里有fly方法,直接调用        bird.fly();        //bird里没有breathe方法,怎么办去父类找        bird.breathe();        System.out.println("-----------------------------");        Fish fish = new Fish();        //fish里没有setName方法,怎么办去父类找        fish.setName("小鱼儿");        //去自己类找发现有就不去父类里找了,这叫重写了父类的方法        fish.breathe();        //fish里没有swimming方法,怎么办去父类找        fish.swimming();    }}

7、String

字符串是引用类型,但是为什么不用new,因为太常用了,就简化了。

如果你不觉的麻烦,也能写成:

String name = new String("name");实际上String name = "name";   就行了

既然是个对象就有属性和方法

他的方法无非就是帮助我们方便的处理这个字符串。

注:使用string一定要注意,必须用一个新的String接受。

String substring = name.substring(1, 3);

案例、这几个最好记住:

符串查找

String 类的 indexOf() 方法在字符串中查找子字符串出现的位置,如过存在返回字符串出现的位置(第一位为0),如果不存在返回 -1。

public class SearchStringEmp {   public static void main(String[] args) {      String strOrig = "xinzhi bigdata Java";      int intIndex = strOrig.indexOf("Java");      if(intIndex == - 1){         System.out.println("没有找到字符串 Java");      }else{         System.out.println("Java 字符串位置 " + intIndex);      }   }}

也可以用contains() 方法

字符串替换

java String 类的 replace 方法可以替换字符串中的字符。

public class test {    public static void main(String args[]){            String str="Hello World,Hello Java.";            System.out.println(str.replace('H','W')); //替换全部            System.out.println(str.replaceFirst("He","Wa")); //替换第一个遇到的            System.out.println(str.replaceAll("He", "Ha")); //替换全部       }}
字符串分割

split(string) 方法通过指定分隔符将字符串分割为数组。

public class test {    public static void main(String args[]){            String str="www-baidu-com";            String delimeter = "-";  //指定分隔符            String[] temp = str.split(delimeter);  //分割字符串            //普通for循环            for(int i =0; i < temp.length; i++){                System.out.println(temp[i]);                System.out.println("");            }                        System.out.println("----java for each循环输出的方法-----");            String str1 = "www.baidu.com";            String delimeter1 = "\\.";   //指定分隔符,.号需要转义,不会明天讲            String[] temp1 = str1.split(delimeter1);            for (String x : temp1){                System.out.println(x);                System.out.println("");            }           }}
字符串截串

substring(string) 方法可以截取从第几个下标(0开始,包含第这一个开始)到第几个下标(不包含)的字符串。

public class test {    public static void main(String args[]){        String name = new String("name");        String substring = name.substring(1, 3);    }}
字符串小写转大写

String toUpperCase() 方法将字符串从小写转为大写。

String str = "string runoob";String strUpper = str.toUpperCase();

8、基础类型的包装类

现在知道有这么个东西就行了,不用练,没几个重要的方法,用的时候讲。

因为基础类型没有方法,不好操作。

所有java对每一种基础类型都进行了包装,生成包装类

int -》 Integer

Integer是个对象,本来是要new

但是太常用了,所以简化了定义的方式,和基础类型一样。

Integer i= 3;

自动装箱 => new Integer(3); 看见变量是个Integer 而后边没new 就自动帮你new一个。

System.out.println(i)

自动拆箱 看见是个包装类,但这里需要打印的是基础类型,自动转。

9、this关键字

this 关键字指内存里分配好空间的(对象)

思维

首先要思考这个工程有哪些角色

1、首先思考这个系统有什么功能 ,首先肯定是一个功能一个方法。2、每一个功能有多复杂,复杂功能能不能拆开,比如一个功能分三步走,每一步能不能单独定义成一个方法。3、思考整个系统的逻辑顺序,什么情况下会被调用。4、有些小功能能不能独立为方法,很多共同的代码,需要抽离成方法。

类的加载(4.20)

1、成员变量与局部变量

成员变量 属性 定义之后有默认初始值基础数据类型 有初始值

基础数据类型,引用数据类型,局部变量必须手动初始化

静态变量全部大写,下划线链接

2、引用传递和值传递

引用传递传的是地址,值传递传的是值

3、基础数据类型包装类

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jWVYVeRt-1635652961164)(JAVA 笔记.assets/image-20210420161225708.png)]

4、自动拆装箱

  • 自动装箱

将基础数据类型转成包装类

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ISchcbk1-1635652961165)(JAVA 笔记.assets/image-20210421081540919.png)]

  • 自动拆箱

将包装类转成基础数据类型

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qpLTSMLD-1635652961166)(JAVA 笔记.assets/image-20210421081620741.png)]

5、类的加载内存图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1wksXh0t-1635652961167)(JAVA 笔记.assets/类加载过程.png)]

继承(4.21)

1、继承

  1. 使用extends关键字实现。

  2. 单继承,一个爹可以有多个儿子,一个儿子只能有一个爹。

  3. 有个 顶级父类叫Object,所有的对象都有一个父类叫Object,这是规定,记住。所以所有的对象都能调用object的所有方法,比如toString()。

  4. new一个子类,一定会先new一个父类,子类的构造方法中第一句默认就是super(),意思就是构造子类一定会先构造一个子类。

  5. 重写父类的方法使用@Override 注解

  6. super

    super指向了引用的父类,可以当作父类的构造器

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HWiyUSe3-1635652961168)(JAVA 笔记.assets/image-20210421114937713.png)]

2、实例图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YV7EkUUq-1635652961169)(JAVA 笔记.assets/image-20210421111925285.png)]

/** * @author zn * @date 2020/2/10 **/public class Father {    public Father(){        System.out.println("爹被创建");    }    public void say(){        System.out.println("我是爹");    }}
/** * @author zn * @date 2020/2/10 **/public class Son extends Father {    public Son(){        super();        System.out.println("儿子被创建!");    }}
/** * @author zn * @date 2020/2/9 **/public class Test {    public static void main(String[] args) {        Son son = new Son();    }}结果--爹被创建儿子被创建!

思想:一个对象调用方法的时候,首先去自己的类里边找,找不到的话就去父类找,父类找也不到,就去父类的父类找,直到Object,找到为止!

类的加载顺序

1.父类的静态属性

​ 类加载的时候就会调用

​ 类加载的时机,第一次主动使用就会主动加载这个类, 被他加载到内存当中

2.父类的静态代码块

3.子类的静态属性

4.子类的静态代码块

5.父类的非静态属性

6.父类的非静态代码块

7.父类的构造器

8.子类的非静态属性

9.子类的非静态代码块

10.子类的构造器

instanceof和对象转型

instanceof

instanceof 判断传的类是否是指定的父类

重写

当父类的方法不满足所需要的要求,就需要进行重写

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iBxIT16O-1635652961170)(JAVA 笔记.assets/image-20210422092959956.png)]

静态方法

静态方法一般当作工具来使用

static 的属性通常当作常量使用,记录一些不变的但是到处可能使用的东西

ToString

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QTllmcuF-1635652961170)(JAVA 笔记.assets/image-20210422173010992.png)]

打印一个类的具体内容具体属性需要重写它的ToString方法

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WKQwcrK2-1635652961171)(JAVA 笔记.assets/image-20210422173326070.png)]

多态

存在的前提

1、要有继承

//先定义一个父类

/** * @author zn * @date 2020/2/9 **/public class  Animal {    private String type;    public Animal(String type) {        this.type = type;    }        //所有的动物都能吃    public  void eat(){        System.out.println("动物在吃食物");    };    public void breathe(){        System.out.println("呼吸");    }    public String getType() {        return type;    }    public void setType(String type) {        this.type = type;    }}
2、要有重写
/** * @author zn * @date 2020/2/9 **///有继承public class Dog extends Animal {    public Dog(String type) {        super(type);    }    //狗有狗的吃方法    //有重写    @Override    public void eat() {        System.out.println(this.getType() + "在吃骨头!");    }    public void enjoy(){        System.out.println("摇尾巴!");    }}
/** * @author zn * @date 2020/2/9 **///有继承public class Cat extends Animal {    public Cat(String type) {        super(type);    }    //有重写    @Override    public void eat() {        System.out.println(this.getType() +"在吃鱼!");    }}
3、要有父类引用指向子类对象
/** * @author zn * @date 2020/2/9 **/public class Test {    public static void main(String[] args) {        //父类引用指向子类对象,子类能完成父类的一切工作        Animal dog= new Dog("小狗");        dog.eat();        //父类引用指向子类对象,子类能完成父类的一切工作        Animal cat = new Cat("小猫");        cat.eat();    }}--结果小狗在吃骨头!小猫在吃鱼!
好处一、灵活
/** * @author zn * @date 2020/2/10 **/public class Girl {    public void KeepAnimal(Animal animal){        System.out.println("小姑娘开始给"+animal.getType()+"喂食。");         System.out.println("小姑娘开始给"+animal.getType()+"喂食。");        //没有重写的方法,调用时就不叫多态        animal.breathe();        //重写了的方法会根据传入的实际的动物调用        animal.eat();    }}
/** * @author zn * @date 2020/2/9 **/public class Test {    public static void main(String[] args) {        Animal dog= new Dog("小狗");        Animal cat = new Cat("小猫");                //武三水养了条狗        Girl wss = new Girl();        wss.KeepAnimal(dog);        //刘慧慧养了一只猫        Girl lhh = new Girl();        lhh.KeepAnimal(cat);                //这样让girl 养动物很灵活    }}

好处有很多,慢慢再体会

抽象类

​ 有些类天然的就是为了让子类继承,而不是为了在父类中调用,这类方法存在的目的就是规定了子类,必须要有这些方法,比如动物类就规定,你实现了我这个类,就必须有eat的方法,而且你必须重写。

​ 对于这种情况,java提出了抽象类,这种类,里边可以有一些方法没有方法体,我们称之为抽象方法,抽象方法必须使用abstract修饰,同时,如果有了abstract抽象方法之后,这个类必须被定义成抽象方法,也使用abstract修饰。

​ 抽象方法是对子类的约束,不管是猫是狗是蛇是龙,他必须都能吃东西。

/** * @author zn * @date 2020/2/9 **/public abstract class  Animal {    private String type;    public Animal(String type) {        this.type = type;    }    //所有的动物都能吃    //规定这是个抽象方法,子类必须实现    public abstract void eat();    public void breathe(){        System.out.println("呼吸");    }    public String getType() {        return type;    }    public void setType(String type) {        this.type = type;    }}

注意:

  1. 不能new,因为有某些方法没有方法体。这些方法就是用来重写的,就是定义了子类必须有这些方法。
  2. 他必须被继承才能用,必须实现所有的抽象方法
  3. 如果一个类继承了抽象类,要么、把自己申明成一个抽象类;要么、实现抽象类的所有方法

要么、实现抽象类的所有方法

接口

当一个类里全是抽象方法时,我们称这类方法叫接口,省略abstract,class改为interface。

注意:接口里省略权限修饰符,默认为public,一个原因是因为他一定是给子类去实现(继承里叫重写)的。

/** * @author zn * @date 2020/2/10 **/public interface Car {    /**     * 汽车能跑,不管什么汽车都能跑,接口里省略权限修饰符,默认为public     */    public void run();    /**     * 汽车能坐人,不管什么汽车都能坐人,接口里省略权限修饰符,默认为public     */    void ride();}
/** * @author zn * @date 2020/2/10 **///接口要被实现,关键字implements,必须实现里边所有的方法public class CRV implements Car {    @Override    public void run() {        System.out.println("CRV 嘟嘟嘟的跑");    }    @Override    public void ride() {		System.out.println("CRV座位就是大,坐着宽敞");    }}
/** * @author zn * @date 2020/2/10 **/public class Passat implements Car {    @Override    public void run() {        System.out.println("帕沙特  啪啪啪的跑");    }    @Override    public void ride() {		System.out.println("CRV座位就是气派,坐着有面子");    }}

java是单继承,但是是多实现,一个类可以实现多个接口,但只能继承一个父类。

实现接口,必须实现接口里全部的方法。

接口里只能有静态常量、抽象方法。

静态常量可以在任何的类、抽象类、接口中定义,它和继承,封装,多态以及new不new没有任何毛线关系。

public static final int num  = 1;

升级版超级数组

1、类名
简单类名:  SuperLinked全类名:    com.zhixin.util.SuperLinked   (包名加类名)
2、包的引入
//该包下的内容全部引入import com.zhixin.util.*;   //只引入一个类import com.zhixin.util.SuperLinked;
3、对象转型

强制转型:从父类的类型强制转为子类,如果传入的真的是该类型,则强转没有问题;如果不是就会报错。

合适的方法是使用instanceof 首先进行判断,确定类型之后再处理。

package com.xinzhi.polymorphism;/** * @author zn * @date 2020/2/10 **/public class Girl {    public void KeepAnimal(Animal animal){        //使用instanceof 判断传进来的对象是不是一个Dog类型        if(animal instanceof Dog){            //强转成某一个子类            Dog dog = (Dog)animal;            dog.enjoy();        }        System.out.println("小姑娘开始给"+animal.getType()+"喂食。");        //没有重写的方法,调用时就不叫多态        animal.breathe();        //重写了的方法会根据传入的实际的动物调用        animal.eat();    }    public void buyCar(Carola car){        System.out.println("小姑娘买车了!发动");        car.run();    }}
4、泛型

  当某个类不知道自己内部处理的数据的真实类型时,可以使用泛型、方法是在类后加,不一定是T,其他字母也行,但一般写成T或E。T就是个未知数,只有当你去new对象时指定了确定的类型时,代码里的T就会变为实际的类型,具体从代码里思考。

二、项目代码

package com.zhixin.util;/** * @author zn * @date 2020/2/7 **/public class SuperArray<T> extends Super<T> {    //维护一个数组,要想什么都存,就要使用顶级父类    private Object[] array;    //当前最后一个数字的下边,要为-1 ,以为数组的第一个下标为0    private int currentIndex = -1;    //构造是初始化    public SuperArray(){        array = new Object[8];    }    //添加数据的方法    public void add(T data){        System.out.println("我是数组的实现!---add");        currentIndex++;        //自动扩容        if(currentIndex > array.length-1){            array = dilatation(array);        }        array[currentIndex] = data;    }    //根据下标查询数字    public T get(int index){        System.out.println("我是数组的实现---get");        return (T)array[index];    }    //查看当前有多少个数字    public int size(){        return currentIndex + 1;    }    //数组扩容的方法    private Object[] dilatation(Object[] oldArray){        Object[] newArray = new Object[oldArray.length * 2];        for (int i = 0; i < oldArray.length; i++) {            newArray[i] = oldArray[i];        }        return newArray;    }    //验证下标是否合法    private boolean validateIndex(int index) {        //只要有一个不满足就返回false        return index <= currentIndex && index >= 0;    }}
package com.zhixin.util;/** * @author zn * @date 2020/2/11 **/public class SuperLinked<T> extends Super<T> {    private Node head = null;    private Node tail = null;    private int length = 0;    //添加元素    public void add(T data){        System.out.println("我是链表的实现-----add");        Node node = new Node<>();        node.setNum(data);        if (length == 0) {            //如果第一次添加一共就一个节点            head = node;        }else{            //和尾巴拉手            tail.setNextNode(node);        }        //把心添加进来的当成尾巴        tail = node;        length ++;    }    //根据下标查询数字,非常有意思的写法    public T get(int index){        System.out.println("我是链表的实现------get");        if(index > length){            return null;        }        //小技巧        Node targetNode = head;        for (int i = 0; i < index; i++) {            targetNode = targetNode.getNextNode();        }        return (T)(targetNode.getNum());    }    //查看当前有多少个数字    public int size(){        return length;    }    class Node {        //存储的真实数据        private T num;        //写一个节点        private Node nextNode = null;        public T getNum() {            return num;        }        public void setNum(T num) {            this.num = num;        }        public Node getNextNode() {            return nextNode;        }        public void setNextNode(Node nextNode) {            this.nextNode = nextNode;        }    }}
package com.zhixin.util;/** * @author zn * @date 2020/2/11 **/public abstract class Super<T> {    /**     * 标记所有的子类实现必须有add方法,添加数据     * @param data     */    public abstract void add(T data);    /**     * 标记所有的子类实现必须有get方法,获取数据     * @param index     * @return     */    public abstract T get(int index);    /**     * 标记所有的子类实现必须有size方法,数据大小     * @return     */    public abstract int size();}

静态内部类(4.24)

使用方法

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AWY2O2BY-1635652961172)(JAVA 笔记.assets/image-20210424091302922.png)]

如果一个类只被某一个类调用,就可以将它放在类的内部,叫做静态内部类。

匿名内部类

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FAGjspCA-1635652961173)(JAVA 笔记.assets/image-20210424093537156.png)]

接口没有名字,却实现了所有方法.

只能用一次,实现一个接口,必须重写

将内部类保存起来,可以用好几次

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cdZaKYZS-1635652961174)(JAVA 笔记.assets/image-20210424093758226.png)]

打jar包和引进jar包

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nf1eqyWg-1635652961175)(JAVA 笔记.assets/image-20210424094932967.png)][外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LKc5B8tk-1635652961176)(JAVA 笔记.assets/image-20210424095041715.png)]

final

被final修饰的类不能被继承

修饰变量的值不能改[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ml66kflM-1635652961177)(JAVA 笔记.assets/image-20210424104828279.png)]

集合与链表

LinkedList 本质是一个链表

ArrayList 本质是一个数组

两者的方法相差不大

public void Testlist(){    List<User> list = new ArrayList<>();    list.add(new User("晓倩",456));    list.remove(new User("晓倩",456));    for (int i = 0; i < list.size() ; i++) {      System.out.println(list.get(i).getUsername() + list.get(i).getPassword());    }}

哈希

Hashcoad

将字符串转换成一组数字[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8svurcpu-1635652961178)(JAVA 笔记.assets/image-20210425084818195.png)]

hash是什么
hash是一个函数,该函数中的实现就是一种算法,就是通过一系列的算法来得到一个hash值,这个时候,我们就需要知道另一个东西,hash表,通过hash算法得到的hash值就在这张hash表中,也就是说,hash表就是所有的hash值组成的,有很多种hash函数,也就代表着有很多种算法得到hash值, 编写散列函数是老生常谈的研究课题,是数学家和理论方面的计算机科学家的研究任务, 我们只需要知道那些比较好用, 大概为啥好用就可以了

hashcode是啥
hashcode就是通过hash函数得来的,通俗的说,就是通过某一种算法得到的,hashcode就是在hash表中有对应的位置。

每个对象都有hashcode,对象的hashcode怎么得来的呢?

首先一个对象肯定有物理地址,网上有人把对象的hashcode说成是对象的地址,事实上这种看法是不全面的,确实有些JVM在实现时是直接返回对象的存储地址,但是大多时候并不是这样,只能说可能存储地址有一定关联,

那么对象如何得到hashcode呢?通过对象的内部地址(也就是物理地址)转换成一个整数,然后该整数通过hash函数的算法就得到了hashcode(不同jvm的实现不同, hotspot的实现贴在了最后),所以,hashcode是什么呢?就是在hash表中对应的位置。这里如果还不是很清楚的话,举个例子,hash表中有 hashcode为1、hashcode为2、(…)3、4、5、6、7、8这样八个位置,有一个对象A,A的物理地址转换为一个整数17(这是假如),就通过直接取余算法,17%8=1,那么A的hashcode就为1,且A就在hash表中1的位置。

为什么使用 HashCode
HashCode的存在主要是为了查找的快捷性, HashCode是用来在散列存储结构中确定对象的存储地址的 ( 用hashcode来代表对象在hash表中的位置 ) , hashCode 存在的重要的原因之一就是在 HashMap(HashSet 其实就是HashMap) 中使用(其实Object 类的 hashCode 方法注释已经说明了 ),HashMap 之所以速度快,因为他使用的是散列表,根据 key 的 hashcode 值生成数组下标(通过内存地址直接查找,不需要判断, 但是需要多出很多内存,相当于以空间换时间)

HashMap

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vJ5IiVys-1635652961179)(JAVA 笔记.assets/image-20210425091928855.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ji20aV2L-1635652961180)(JAVA 笔记.assets/image-20210425085027641.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ggZkIhk9-1635652961181)(JAVA 笔记.assets/image-20210425084737456.png)]

在HashMap中如果Key相同 就将前一个覆盖

遍历HashMap

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fdHU1aNE-1635652961182)(JAVA 笔记.assets/image-20210425090605462.png)]

Hash是无序的,不能排序

HashSet

存储时只有Key 无value,不可以存重复的值

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-K54MnWyh-1635652961183)(JAVA 笔记.assets/image-20210425091742912.png)]

因为无下标使用增强for遍历

迭代器

iterator

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zphU9frG-1635652961184)(JAVA 笔记.assets/image-20210425163548845.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TvgcasHX-1635652961186)(JAVA 笔记.assets/image-20210425163104329.png)]

HashMap使用迭代器遍历

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nD14ojyE-1635652961187)(JAVA 笔记.assets/image-20210425170434967.png)]

!!!使用迭代器删除一个元素

Java中Collection和Collections的区别

1、java.util.Collection 是一个集合接口(集合类的一个顶级接口)。它提供了对集合对象进行基本操作的通用接口方法。Collection接口在Java 类库中有很多具体的实现。Collection接口的意义是为各种具体的集合提供了最大化的统一操作方式,其直接继承接口有List与Set。

2、Collections则是集合类的一个工具类/帮助类,其中提供了一系列静态方法,用于对集合中元素进行排序、搜索以及线程安全等各种操作。

可变参数

public int sun (...nums){} //...nums代表可变参数

可变参数必须放在最后,一个方法只能有一个[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NYdxm2eh-1635652961188)(JAVA 笔记.assets/image-20210427154149908.png)]

Data方法

时间戳(Timestamp)

时间戳是指格林威治时间1970年01月01日00时00分00秒(北京时间1970年01月01日8时00分00秒)起止到现在的总毫秒数

Data使用

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cQwuIdmP-1635652961188)(JAVA 笔记.assets/image-20210427161345111.png)]

日历类Calender

创建 Calendar 对象
Calendar 是一个抽象类, 无法通过直接实例化得到对象. 因此, Calendar 提供了一个方法 getInstance,来获得一个Calendar对象, 得到的 Calendar 由当前时间初始化.

Calendar cal = Calendar.getInstance();

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GLpOIlCg-1635652961189)(JAVA 笔记.assets/20181205012638154.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3a7UKYLb-1635652961190)(JAVA 笔记.assets/20181205012900902.png)]

月数从0开始(0,1,2,3,4,5,6,7,8,9,10,11)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TxbRIxH0-1635652961191)(JAVA 笔记.assets/image-20210502104405044.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KZNJVYtW-1635652961192)(JAVA 笔记.assets/image-20210502104435087.png)]

设计模式(5.2)

最常问的(抽象工厂,代理,单例)

单例

堆内存中只能有一个实例对象,就叫做单例

单例模式
单例模式(Singleton Pattern)是Java中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。
1、单例类只能有一个实例。
2、单例类必须自己创建自己的唯一实例。
3、单例类必须给所有其他对象提供这—实例。
意图: 保证一个类仅有一个实例,并提供一个访问它的全局访问点。主要解决:一个全局使用的类频繁地创建与销毁。
何时使用: 当您想控制实例数目,节省系统资源的时候。
如何解决: 判断系统是否已经有这个单例,如果有则返回,如果没有则创建。关键代码:构造函数是私有的。

饿汉式单例

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UdCZ0KAY-1635652961193)(JAVA 笔记.assets/image-20210502113501649.png)]

是线程安全的

懒汉式单例

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vQ89sLow-1635652961194)(JAVA 笔记.assets/image-20210502130338279.png)]

归并的思想

public static int[] Lianjie(int[] a1,int[] a2){    //定义一个新数组    int[] tamp = new int[a1.length + a2.length];    int left = 0;    int right = 0;    int con = 0;	    while (a1.length != left){        if (a1[left] > a2[right]){            tamp[con] = a2[right];            right++;        }else {            tamp[con] = a1[left];            left++;        }        con++;    }    if (con >= a1.length){        for (int i = right; i < a2.length ; i++) {            tamp[con++] = a2[i];        }    }    if (con >= a2.length){        for (int i = left; i < a1.length ; i++) {            tamp[con++] = a1[i];        }    }    return tamp;}

线程(Thread)

进程和线程的区别:一个软件就是一个进程,一个进程可以运行多个线程,我们可以使用多个线程同时执行提高效率。

多个线程同时执行的时候,互相之间没有必然的顺序,他是有cpu的调度决定的。

启动线程必须调用start方法,不能调用run,调用run只是方法调用,start是个native的本地方法,会调用cpu的资源,开辟线程。

synchronized保证线程安全

List线程不安全,Vector是线程安全的

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wnPBHaCE-1635652961195)(JAVA 笔记.assets/image-20210503112117934.png)]

HashMap不是线程安全的,HashTable是线程安全的

String、StringBuffer、StringBuilder

和String类不同的是,StringBuffer和StringBuilder类的对象能够被多次的修改,并且不产生新的未使用对象。
StringBuilder类在Java 5中被提出,它和StringBuffer之间的最大不同在于 StringBuilder的方法不是线程安全的(不能同步访问)。由于StringBuilder 相较于StringBuffer有速度优势,==所以多数情况下建议使用StringBuilder类。==然而在应用程序要求线程安全的情况下,则必须使用 StringBuffer类。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sx0bCmmm-1635652961195)(JAVA 笔记.assets/image-20210503120204509.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nulESlZT-1635652961196)(JAVA 笔记.assets/image-20210503121248118.png)]

UUAD

生成一个世界上唯一的字符串

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FO9LKSlJ-1635652961197)(JAVA 笔记.assets/image-20210503121740825.png)]

异常

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IqJmxmtv-1635652961197)(JAVA 笔记.assets/image-20210503123907041.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pQiI5EKF-1635652961198)(JAVA 笔记.assets/image-20210503124017044.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xxSHXpGu-1635652961198)(JAVA 笔记.assets/image-20210503124434268.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hVxVvkoc-1635652961199)(JAVA 笔记.assets/image-20210503124457226.png)]

InterruptedException

将异常向上一级抛出

public static void main(String[] args) throws InterruptedException {    time();}public static void time() throws InterruptedException {    Thread.sleep(200);}

try{ }catch( ){ }

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hOKzn4Ce-1635652961200)(JAVA 笔记.assets/image-20210504094911509.png)]

如果一个异常继承自Exception,则为检查性异常

==printStackTrace()==方法的意思是:在命令行打印异常信息在程序中出错的位置及原因。

System.out.println(e),这个方法打印出异常,并且输出在哪里出现的异常,不过它和另外一个e.printStackTrace()方法不同。后者也是打印出异常,但是它还将显示出更深的调用信息

2.throw的作用

手动抛出异常但是有时候有些错误在jvm看来不是错误,比如说
int age = 0;
age = -100;
System.out.println(age);
很正常的整形变量赋值,但是在我们眼中看来就不正常,谁的年龄会是负的呢。所以我们需要自己手动引发异常,这就是throw的作用
int age = 0;
age = -100;
if(age<0)
{
Exception e = new Exception();//创建异常对象
throw e;//抛出异常
}
System.out.println(age);

3.throws的作用

声明方法可能回避的异常有异常被抛出了,就要做处理,所以java中有try-catch可是有时候一个方法中产生了异常,但是不知道该怎么处理它,那么就放着不管,当有异常抛出时会中断该方法,而异常被抛到这个方法的调用者那里。这个有点像下属处理不了的问题就交到上司手里一样,这种情况称为回避异常但是这使得调用这个方法就有了危险,因为谁也不知道这个方法什么时候会丢一个什么样的异常给调用者,所以在定义方法时,就需要在方法头部分使用throws来声明这个方法可能回避的异常

throws和throw

**throws:

用来声明一个方法可能产生的所有异常,不做任何处理而是将异常往上传,谁调用我我就抛给谁。
用在方法声明后面,跟的是异常类名
可以跟多个异常类名,用逗号隔开
表示抛出异常,由该方法的调用者来处理
throws表示出现异常的一种可能性,并不一定会发生这些异常
throw:

则是用来抛出一个具体的异常类型。
用在方法体内,跟的是异常对象名
只能抛出一个异常对象名
表示抛出异常,由方法体内的语句处理
throw则是抛出了异常,执行throw则一定抛出了某种异常

运行时异常

ArithmeticException 算数异常

ClassCastException:类转换异常

NullPointerExceptionextends 空指针异常

检查性异常

ClassNotFoundException,未找到类

InterruptedException,中断异常

ioException

SQLException,

TimeoutException,

FileNotFoundException

java IO入门

一、 java IO 流的概念

java的io是实现输入和输出的基础,可以方便的实现数据的输入和输出操作。在java中把不同的输入/输出源(键盘,文件,网络连接等)抽象表述为“流”(stream)。

二、 Io流的分类:

按照不同的分类方式,可以把流分为不同的类型。常用的分类有三种:

1、 按照流的流向分,可以分为输入流和输出流。

  • 输入流: 只能从中读取数据,而不能向其写入数据。
  • 输出流:只能向其写入数据,而不能向其读取数据。
    此处的输入,输出涉及一个方向的问题,对于如图15.1所示的数据流向,数据从内存到硬盘,通常称为输出流——也就是说,这里的输入,输出都是从程序运行所在的内存的角度来划分的。

对于如图15.2所示的数据流向,数据从服务器通过网络流向客户端,在这种情况下,Server端的内存负责将数据输出到网络里,因此Server端的程序使用输出流;Client端的内存负责从网络中读取数据,因此Client端的程序应该使用输入流。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YNAM9GxH-1635652961201)(JAVA 笔记.assets/11798292-1268450dfccafde7.png)]

2 、按照操作单元划分,可以划分为字节流和字符流。

字节流和字符流的用法几乎完成全一样,区别在于字节流和字符流所操作的数据单元不同,字节流操作的单元是数据单元是8位的字节,字符流操作的是数据单元为16位的字符。

字节流主要是由InputStream和outPutStream作为基类,而字符流则主要有Reader和Writer作为基类。

3、 按照流的角色划分为节点流和处理流。

可以从/向一个特定的IO设备(如磁盘,网络)读/写数据的流,称为节点流。节点流也被称为低级流。图15.3显示了节点流的示意图。哪里需要怼哪里。

从图15.3中可以看出,当使用节点流进行输入和输出时,程序直接连接到实际的数据源,和实际的输入/输出节点连接。

处理流则用于对一个已存在的流进行连接和封装,通过封装后的流来实现数据的读/写功能。处理流也被称为高级流。图15.4显示了处理流的示意图。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gRH2LpQS-1635652961201)(JAVA 笔记.assets/11798292-52e163c670a11e01.png)]

4、java输入/输出流体系中常用的流的分类表

| 分类 | 字节输入流 |字节输出流|字符输入流|字符输出流|

分类 字节输入流 字节输出流 字符输入流 字符输出流
抽象基类 InputStream OutputStream Reader Writer
访问文件 FileInputStream FileOutputStream FileReader FileWriter
访问数组 ByteArrayInputStream ByteArrayOutputStream CharArrayReader CharArrayWriter
访问字符串 StringReader StringWriter
缓冲流 BufferedInputStream BufferedOutputStream BufferedReader BufferedWriter
转换流 InputStreamReader OutputStreamWriter
对象流 ObjectInputStream ObjectOutputStream

转换流能都将字符流转成字节流

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-u6nEVDyN-1635652961202)(JAVA 笔记.assets/20161220152558257)]

三、具体操作

1、文件的基本操作

@Testpublic void testFile() throws Exception{    //创建文件    File file = new File("E:\\test\\b.txt");    file.createNewFile();    //查看文件夹下的文件    File file2 = new File("E:\\test\\b.txt");    String[] list = file2.list();    for (int i = 0; i < list.length; i++) {        System.out.println(list[i]);    }    //其他的方法自己查看}

2、复制文件带有进度条

@Testpublic void testFileInputStream() throws Exception {    File file = new File("E:\\test\\a\\233.mp4");    //拿到文件的大小    long dataLength = file.length();    //构建一个输入流,他的数据要流入内存,咱们的程序    InputStream inputStream = new FileInputStream(file);    //构建一个输出流,他的数据要从内存(咱们的程序)流到另一个文件夹    OutputStream outputStream = new FileOutputStream("E:\\test\\b\\233.mp4");    //新建一个水泵,能存一点水,每次对对1k    byte[] buffer = new byte[1024 *1024*50];    Long currentLength = 0L;    //如果read返回-1说明读完了    int len;    int showNumber = 0;    while ( (len = inputStream.read(buffer)) != -1 ){        outputStream.write(buffer,0,len);        currentLength += len;        //当下加载了百分之多少        int currentPer  = (int)(((double)currentLength/dataLength)*100);        //目的是不重复显示        if(showNumber !=  currentPer){            showNumber = currentPer;            System.out.println("已经拷贝了百分之" + showNumber);        }    }    outputStream.flush();    outputStream.close();    inputStream.close();}

3、字节流读文件

@Testpublic void testInputStream() throws Exception{    //怼了一个输入流到文件上    InputStream wordInput = new FileInputStream("E:\\test\\a\\word.txt");    //建立缓冲区    byte[] bytes = new byte[1024];    int len;    while ( (len = wordInput.read(bytes)) != -1 ){        System.out.println(new String(bytes,0,len, Charset.forName("ISO8859-1")));    }    wordInput.close();}

4、字符流对文件

@Testpublic void testReader() throws Exception{    //怼了一个输入流到文件上    Reader reader = new FileReader("E:\\test\\a\\word.txt");    BufferedReader br = new BufferedReader(reader);    String str;    while ((str = br.readLine()) != null){        System.out.println(str);    }    reader.close();    br.close();}

5、向文件里写内容

//这个用main方法测吧public void testWriter() throws Exception{    //怼了一个输入流到文件上    Writer writer = new FileWriter("E:\\test\\a\\writer.txt");    BufferedWriter bw = new BufferedWriter(writer);    Scanner scanner = new Scanner(System.in);    while (true){        System.out.print("请输入:");        String words = scanner.next();        bw.write(words);        bw.flush();    }}

6、StringReader就是往String上怼

@Testpublic void testStringReader() throws Exception{    //怼了一个string    OutputStream os = new FileOutputStream("E:\\test\\a\\user.txt");    ObjectOutput oo  = new ObjectOutputStream(os);    oo.writeObject(new User("王老师",3,4));    oo.flush();    oo.close();    os.close();}

7、对象流就是对new出来的对象进行序列化

该对象必须实现Serializable接口,才能被序列化。

对象在内存就是一堆0和1,任何文件在内存都是0和1,都能转化成字节数组,进行保存或者网络传输。

对象序列化也是一样的,就是把内存的对象以字节数组的形式上保存,我们能保存在磁盘,或者在网络中传输。

import java.io.Serializable;/** * @author zn * @date 2020/2/14 **/public class User implements Serializable {    private String name;    private int age;    private int gander;    public User(String name, int age, int gander) {        this.name = name;        this.age = age;        this.gander = gander;    }    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }    public int getAge() {        return age;    }    public void setAge(int age) {        this.age = age;    }    public int getGander() {        return gander;    }    public void setGander(int gander) {        this.gander = gander;    }}
@Testpublic void testObjectOut() throws Exception{    //怼了一个string    InputStream is = new FileInputStream("E:\\test\\a\\user.txt");    ObjectInputStream oi  = new ObjectInputStream(is);    User user = (User)(oi.readObject());    System.out.println(user.getName());    is.close();    oi.close();}

四、flush()方法介绍

查阅文档可以发现,IO流中每一个类都实现了Closeable接口,它们进行资源操作之后都需要执行close()方法将流关闭 。但字节流与字符流的不同之处在于:字节流是直接与数据产生交互,而字符流在与数据交互之前要经过一个缓冲区
草图:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GS39TjAw-1635652961203)(JAVA 笔记.assets/20160825235002516)]

使用字符流对资源进行操作的时候,如果不使用close()方法,则读取的数据将保存在缓冲区中,要清空缓冲区中的数据有两种办法:

  • public abstract void close() throws IOException
    关闭流的同时将清空缓冲区中的数据,该抽象方法由具体的子类实现

  • public abstract void flush() throws IOException
    不关闭流的话,使用此方法可以清空缓冲区中的数据,但要注意的是,此方法只有Writer类或其子类拥有,而在Reader类中并没有提供。此方法同样是在具体的子类中进行实现 。

客户端与服务器

服务器

public class QQsocket {    public static void main(String[] args) {        ServerSocket server;        InputStream inputStream = null;        try {                //创建一个服务器                server = new ServerSocket();                //连接一个端口                server.bind(new InetSocketAddress(8888));                //等待输入数据                Socket acc = server.accept();                //等待用流接收                 inputStream = acc.getInputStream();                byte[] by = new byte[1024];                int len ;                while ((len = inputStream.read(by) )!= -1){                    System.out.println(new String(by,0,len));                }            } catch (IOException e) {                e.printStackTrace();            }finally {                if (inputStream != null){                    try {                                         inputStream.close();                    } catch (IOException e) {                        e.printStackTrace();                    }                }            }    }

客户端

public class qq {    public static void main(String[] args) {        Socket socket = new Socket();        OutputStream outputStream = null;        try {            socket.connect(new InetSocketAddress("172.16.1.243", 8888));             outputStream = socket.getOutputStream();            String nas = "早上好~~~晓倩!";            outputStream.write(nas.getBytes());        } catch (IOException e) {            e.printStackTrace();        }finally {            if (outputStream != null){                try {                    outputStream.flush();                    outputStream.close();                } catch (IOException e) {                    e.printStackTrace();                }            }        }    }}

序列化和反序列化

1、什么是序列化和反序列化

(1)Java序列化是指把Java对象转换为字节序列的过程,而Java反序列化是指把字节序列恢复为Java对象的过程;

(2)**序列化:**对象序列化的最主要的用处就是在传递和保存对象的时候,保证对象的完整性和可传递性。序列化是把对象转换成有序字节流,以便在网络上传输或者保存在本地文件中。序列化后的字节流保存了Java对象的状态以及相关的描述信息。序列化机制的核心作用就是对象状态的保存与重建。

(3)**反序列化:**客户端从文件中或网络上获得序列化后的对象字节流后,根据字节流中所保存的对象状态及描述信息,通过反序列化重建对象。

(4)本质上讲,序列化就是把实体对象状态按照一定的格式写入到有序字节流,反序列化就是从有序字节流重建对象,恢复对象状态。

2、为什么需要序列化与反序列化

我们知道,当两个进程进行远程通信时,可以相互发送各种类型的数据,包括文本、图片、音频、视频等, 而这些数据都会以二进制序列的形式在网络上传送。

那么当两个Java进程进行通信时,能否实现进程间的对象传送呢?答案是可以的!如何做到呢?这就需要Java序列化与反序列化了!

换句话说,一方面,发送方需要把这个Java对象转换为字节序列,然后在网络上传送;另一方面,接收方需要从字节序列中恢复出Java对象。

当我们明晰了为什么需要Java序列化和反序列化后,我们很自然地会想Java序列化的好处。其好处一是实现了数据的持久化,通过序列化可以把数据永久地保存到硬盘上(通常存放在文件里),二是,利用序列化实现远程通信,即在网络上传送对象的字节序列。

总的来说可以归结为以下几点:

  • 永久性保存对象,保存对象的字节序列到本地文件或者数据库中;
  • 通过序列化以字节流的形式使对象在网络中进行传递和接收;
  • 通过序列化在进程间传递对象;

enum枚举

什么是枚举

我们学习过单例模式,即一个类只有一个实例。而枚举其实就是多例,一个类有多个实例,但实例的个数不是无穷的,是有限个数的。例如word文档的对齐方式有几种:左对齐、居中对齐、右对齐。开车的方向有几种:前、后、左、右!
 我们称呼枚举类中实例为枚举项!一般一个枚举类的枚举项的个数不应该太多,如果一个枚举类有30个枚举项就太多了!

定义枚举类型

定义枚举类型需要使用enum关键字,例如:

public enum Direction {    FRONT, BEHIND, LEFT, RIGHT;}Direction d = Direction.FRONT;

注意,定义枚举类的关键字是enum,而不是Enum,所有关键字都是小写的!
其中FRONT、BEHIND、LEFT、RIGHT都是枚举项,它们都是本类的实例,本类一共就只有四个实例对象。
在定义枚举项时,多个枚举项之间使用逗号分隔,最后一个枚举项后需要给出分号!但如果枚举类中只有枚举项(没有构造器、方法、实例变量),那么可以省略分号!建议不要省略分号!
不能使用new来创建枚举类的对象,因为枚举类中的实例就是类中的枚举项,所以在类外只能使用类名.枚举项。

枚举与switch

枚举类型可以在switch中使用

Direction d = Direction.FRONT;  switch(d) {    case FRONT: System.out.println("前面");break;    case BEHIND:System.out.println("后面");break;    case LEFT:  System.out.println("左面");break;    case RIGHT: System.out.println("右面");break;    default:System.out.println("错误的方向");}Direction d1 = d;System.out.println(d1);

注意,在switch中,不能使用枚举类名称,例如:“case Direction.FRONT:”这是错误的,因为编译器会根据switch中d的类型来判定每个枚举类型,在case中必须直接给出与d相同类型的枚举选项,而不能再有类型。

  1. 所有枚举类都是Enum的子类
    所有枚举类都默认是Enum类的子类,无需我们使用extends来继承。这说明Enum中的方法所有枚举类都拥有。
  • int compareTo(E e):比较两个枚举常量谁大谁小,其实比较的就是枚举常量在枚举类中声明的顺序,例如FRONT的下标为0,BEHIND下标为1,那么FRONT小于BEHIND;
  • boolean equals(Object o):比较两个枚举常量是否相等;
  • int hashCode():返回枚举常量的hashCode;
  • String name():返回枚举常量的名字;
  • int ordinal():返回枚举常量在枚举类中声明的序号,第一个枚举常量序号为0;
  • String toString():把枚举常量转换成字符串;
  • static T valueOf(Class enumType, String name):把字符串转换成枚举常量。

枚举类的构造器

枚举类也可以有构造器,构造器默认都是private修饰,而且只能是private。因为枚举类的实例不能让外界来创建!

enum Direction {    FRONT, BEHIND, LEFT, RIGHT;//[在枚举常量后面必须添加分号,因为在枚举常量后面还有其他成员时,分号是必须的。枚举常量必须在枚举类中所有成员的上方声明。]        Direction()//[枚举类的构造器不可以添加访问修饰符,枚举类的构造器默认是private的。但你自己不能添加private来修饰构造器。] {        System.out.println("hello");    }}

其实创建枚举项就等同于调用本类的无参构造器,所以FRONT、BEHIND、LEFT、RIGHT四个枚举项等同于调用了四次无参构造器,所以你会看到四个hello输出。

枚举类可以有成员

其实枚举类和正常的类一样,可以有实例变量,实例方法,静态方法等等,只不过它的实例个数是有限的,不能再创建实例而已。

enum Direction {    FRONT("front"), BEHIND("behind"), LEFT("left"), RIGHT("right");        private String name;        Direction(String name) {        this.name = name;    }        public String getName() {        return name;    }}Direction d = Direction.FRONT;System.out.println(d.getName());

因为Direction类只有唯一的构造器,并且是有参的构造器,所以在创建枚举项时,必须为构造器赋值:FRONT(“front”),其中”front”就是传递给构造器的参数。你不要鄙视这种语法,你应该做的是接受这种语法!
Direction类中还有一个实例域:String name,我们在构造器中为其赋值,而且本类还提供了getName()这个实例方法,它会返回name的值。

  1. 枚举类中还可以有抽象方法
    还可以在枚举类中给出抽象方法,然后在创建每个枚举项时使用“特殊”的语法来重复抽象方法。所谓“特殊”语法就是匿名内部类!也就是说每个枚举项都是一个匿名类的子类对象!

通常fun()方法应该定义为抽象的方法,因为每个枚举常量都会去重写它。
你无法把Direction声明为抽象类,但需要声明fun()方法为抽象方法。

enum Direction {    FRONT() {        public void fun() {            System.out.println("FROND:重写了fun()方法");        }    },     BEHIND() {        public void fun() {            System.out.println("BEHIND:重写了fun()方法");        }    },     LEFT() {        public void fun() {            System.out.println("LEFT:重写了fun()方法");        }    },    RIGHT() {        public void fun() {            System.out.println("RIGHT:重写了fun()方法");        }    };        public abstract void fun()[只需要把fun()方法修改为抽象方法,但不可以把Direction类声明为抽象类。];}
  1. 每个枚举类都有两个特殊方法
    每个枚举类都有两个不用声明就可以调用的static方法,而且这两个方法不是父类中的方法。这又是枚举类特殊的地方,下面是Direction类的特殊方法。
  • static Direction[] values():返回本类所有枚举常量;
  • static Direction valueOf(String name):通过枚举常量的名字返回Direction常量,注意,这个方法与Enum类中的valueOf()方法的参数个数不同。

泛型

一、什么是泛型?

Java泛型设计原则:只要在编译时期没有出现警告,那么运行时期就不会出现ClassCastException异常.

泛型:把类型明确的工作推迟到创建对象或调用方法的时候才去明确的特殊的类型

参数化类型:

  • 把类型当作是参数一样传递
  • <数据类型> 只能是引用类型

相关术语:

  • ArrayList中的E称为类型参数变量
  • ArrayList中的Integer称为实际类型参数
  • 整个称为ArrayList泛型类型
  • 整个ArrayList称为参数化的类型ParameterizedType

二、为什么需要泛型

早期Java是使用Object来代表任意类型的,但是向下转型有强转的问题,这样程序就不太安全

首先,我们来试想一下:没有泛型,集合会怎么样

  • Collection、Map集合对元素的类型是没有任何限制的。本来我的Collection集合装载的是全部的Dog对象,但是外边把Cat对象存储到集合中,是没有任何语法错误的。
  • 把对象扔进集合中,集合是不知道元素的类型是什么的,仅仅知道是Object。因此在get()的时候,返回的是Object。外边获取该对象,还需要强制转换

有了泛型以后:

  • 代码更加简洁【不用强制转换】
  • 程序更加健壮【只要编译时期没有警告,那么运行时期就不会出现ClassCastException异常】
  • 可读性和稳定性【在编写集合的时候,就限定了类型】

2.1有了泛型后使用增强for遍历集合

在创建集合的时候,我们明确了集合的类型了,所以我们可以使用增强for来遍历集合!

        //创建集合对象        ArrayList list = new ArrayList<>();        list.add("hello");        list.add("world");        list.add("java");        //遍历,由于明确了类型.我们可以增强for        for (String s : list) {            System.out.println(s);        }

三、泛型基础

3.1泛型类

泛型类就是把泛型定义在类上,用户使用该类的时候,才把类型明确下来…这样的话,用户明确了什么类型,该类就代表着什么类型…用户在使用的时候就不用担心强转的问题,运行时转换异常的问题了。

  • 在类上定义的泛型,在类的方法中也可以使用!
/*    1:把泛型定义在类上    2:类型变量定义在类上,方法中也可以使用 */public class ObjectTool<T> {    private T obj;    public T getObj() {        return obj;    }    public void setObj(T obj) {        this.obj = obj;    }}
  • 测试代码:

用户想要使用哪种类型,就在创建的时候指定类型。使用的时候,该类就会自动转换成用户想要使用的类型了。

    public static void main(String[] args) {        //创建对象并指定元素类型        ObjectTool tool = new ObjectTool<>();        tool.setObj(new String("钟福成"));        String s = tool.getObj();        System.out.println(s);        //创建对象并指定元素类型        ObjectTool objectTool = new ObjectTool<>();        /**         * 如果我在这个对象里传入的是String类型的,它在编译时期就通过不了了.         */        objectTool.setObj(10);        int i = objectTool.getObj();        System.out.println(i);    }

3.2泛型方法

前面已经介绍了泛型类了,在类上定义的泛型,在方法中也可以使用…

现在呢,我们可能就仅仅在某一个方法上需要使用泛型外界仅仅是关心该方法,不关心类其他的属性…这样的话,我们在整个类上定义泛型,未免就有些大题小作了。

  • 定义泛型方法…泛型是先定义后使用的
    //定义泛型方法..    public  void show(T t) {        System.out.println(t);    }
  • 测试代码:

用户传递进来的是什么类型,返回值就是什么类型了

    public static void main(String[] args) {        //创建对象        ObjectTool tool = new ObjectTool();        //调用方法,传入的参数是什么类型,返回值就是什么类型        tool.show("hello");        tool.show(12);        tool.show(12.5);    }

3.3泛型类派生出的子类

前面我们已经定义了泛型类,泛型类是拥有泛型这个特性的类,它本质上还是一个Java类,那么它就可以被继承

那它是怎么被继承的呢??这里分两种情况

  1. 子类明确泛型类的类型参数变量
  2. 子类不明确泛型类的类型参数变量

3.3.1子类明确泛型类的类型参数变量

  • 泛型接口
/*    把泛型定义在接口上 */public interface Inter<T> {    public abstract void show(T t);}
  • 实现泛型接口的类…
/** * 子类明确泛型类的类型参数变量: */public class InterImpl implements Inter<String> {    @Override    public void show(String s) {        System.out.println(s);    }}

3.3.2子类不明确泛型类的类型参数变量

  • 当子类不明确泛型类的类型参数变量时,外界使用子类的时候,也需要传递类型参数变量进来,在实现类上需要定义出类型参数变量
/** * 子类不明确泛型类的类型参数变量: *      实现类也要定义出类型的 * */public class InterImpl<T> implements Inter<T> {    @Override    public void show(T t) {        System.out.println(t);    }}

测试代码:

    public static void main(String[] args) {        //测试第一种情况        //Inter i = new InterImpl();        //i.show("hello");        //第二种情况测试        Inter ii = new InterImpl<>();        ii.show("100");    }

值得注意的是:

  • 实现类的要是重写父类的方法,返回值的类型是要和父类一样的!
  • 类上声明的泛形只对非静态成员有效

3.4类型通配符

为什么需要类型通配符????我们来看一个需求…

现在有个需求:方法接收一个集合参数,遍历集合并把集合元素打印出来,怎么办?

  • 按照我们没有学习泛型之前,我们可能会这样做:
public void test(List list){    for(int i=0;i<list.size();i++){                System.out.println(list.get(i));        }}

上面的代码是正确的,只不过在编译的时候会出现警告,说没有确定集合元素的类型…这样是不优雅的…

  • 那我们学习了泛型了,现在要怎么做呢??有的人可能会这样做:
public void test(List<Object> list){    for(int i=0;i<list.size();i++){                System.out.println(list.get(i));        }}

这样做语法是没毛病的,但是这里十分值得注意的是:该test()方法只能遍历装载着Object的集合!!!

强调:泛型中的并不是像以前那样有继承关系的,也就是说ListList是毫无关系的!!!!

那现在咋办???我们是不清楚List集合装载的元素是什么类型的,List这样是行不通的…于是Java泛型提供了类型通配符 ?

所以代码应该改成这样:

public void test(List<?> list){    for(int i=0;i<list.size();i++){                System.out.println(list.get(i));        }}

?号通配符表示可以匹配任意类型,任意的Java类都可以匹配

现在非常值得注意的是,当我们使用?号通配符的时候:就只能调对象与类型无关的方法,不能调用对象与类型有关的方法。

记住,只能调用与对象无关的方法,不能调用对象与类型有关的方法。因为直到外界使用才知道具体的类型是什么。也就是说,在上面的List集合,我是不能使用add()方法的。因为add()方法是把对象丢进集合中,而现在我是不知道对象的类型是什么。


3.4.1设定通配符上限

首先,我们来看一下设定通配符上限用在哪里…

现在,我想接收一个List集合,它只能操作数字类型的元素【Float、Integer、Double、Byte等数字类型都行】,怎么做???

我们学习了通配符,但是如果直接使用通配符的话,该集合就不是只能操作数字了。因此我们需要用到设定通配符上限

List<? extends Number>

上面的代码表示的是:List集合装载的元素只能是Number的子类或自身

    public static void main(String[] args) {        //List集合装载的是Integer,可以调用该方法        List integer = new ArrayList<>();        test(integer);        //List集合装载的是String,在编译时期就报错了        List strings = new ArrayList<>();        test(strings);    }    public static void test(List list) {            }

3.4.2设定通配符下限

既然上面我们已经说了如何设定通配符的上限,那么设定通配符的下限也不是陌生的事了。直接来看语法吧

    //传递进来的只能是Type或Type的父类    

设定通配符的下限这并不少见,在TreeSet集合中就有…我们来看一下

    public TreeSet(Comparator<? super E> comparator) {        this(new TreeMap<>(comparator));    }

那它有什么用呢??我们来想一下,当我们想要创建一个TreeSet类型的变量的时候,并传入一个可以比较String大小的Comparator。

那么这个Comparator的选择就有很多了,它可以是Comparator,还可以是类型参数是String的父类,比如说Comparator

这样做,就非常灵活了。也就是说,只要它能够比较字符串大小,就行了

经评论去补充:在泛型的上限和下限中有一个原则:PECS(Producer Extends Consumer Super)

书上是这样写的:

带有子类限定的可以从泛型读取【也就是—>(? extend T)】-------->Producer Extends
带有超类限定的可以从泛型写入【也就是—>(? super T)】-------->Consumer Super
也有相关博文写得很好:

http://blog.51cto.com/flyingc…
https://blog.csdn.net/xx32666…

3.5通配符和泛型方法

大多时候,我们都可以使用泛型方法来代替通配符的

    //使用通配符    public static void test(List list) {    }    //使用泛型方法    public  void  test2(List t) {            }

上面这两个方法都是可以的…那么现在问题来了,我们使用通配符还是使用泛型方法呢??

原则:

  • 如果参数之间的类型有依赖关系,或者返回值是与参数之间有依赖关系的。那么就使用泛型方法
  • 如果没有依赖关系的,就使用通配符,通配符会灵活一些.

3.6泛型擦除

泛型是提供给javac编译器使用的,它用于限定集合的输入类型,让编译器在源代码级别上,即挡住向集合中插入非法数据。但编译器编译完带有泛形的java程序后,生成的class文件中将不再带有泛形信息,以此使程序运行效率不受到影响,这个过程称之为“擦除”。

3.6.1兼容性

JDK5提出了泛型这个概念,但是JDK5以前是没有泛型的。也就是泛型是需要兼容JDK5以下的集合的。

当把带有泛型特性的集合赋值给老版本的集合时候,会把泛型给擦除了。

值得注意的是:它保留的就类型参数的上限。

        List<String> list = new ArrayList<>();        //类型被擦除了,保留的是类型的上限,String的上限就是Object        List list1 = list;

如果我把没有类型参数的集合赋值给带有类型参数的集合赋值,这又会怎么样??

        List list = new ArrayList();        List<String> list2 = list;

它也不会报错,仅仅是提示“未经检查的转换”


四、泛型的应用

当我们写网页的时候,常常会有多个DAO,我们要写每次都要写好几个DAO,这样会有点麻烦。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LLYKADqE-1635652961204)(JAVA 笔记.assets/162810c81b07499d)]

那么我们想要的效果是什么呢??只写一个抽象DAO,别的DAO只要继承该抽象DAO,就有对应的方法了。

要实现这样的效果,肯定是要用到泛型的。因为在抽象DAO中,是不可能知道哪一个DAO会继承它自己,所以是不知道其具体的类型的。而泛型就是在创建的时候才指定其具体的类型。

  • 抽象DAO
public abstract class BaseDao<T> {    //模拟hibernate....    private Session session;    private Class clazz;            //哪个子类调的这个方法,得到的class就是子类处理的类型(非常重要)    public BaseDao(){        Class clazz = this.getClass();  //拿到的是子类        ParameterizedType  pt = (ParameterizedType) clazz.getGenericSuperclass();  //BaseDao        clazz = (Class) pt.getActualTypeArguments()[0];        System.out.println(clazz);            }        public void add(T t){        session.save(t);    }        public T find(String id){        return (T) session.get(clazz, id);    }        public void update(T t){        session.update(t);    }        public void delete(String id){        T t = (T) session.get(clazz, id);        session.delete(t);    }    }
  • 继承抽象DAO,该实现类就有对应的增删改查的方法了。

CategoryDao

public class CategoryDao extends BaseDao<Category> {}

BookDao

public class BookDao extends BaseDao<Book> {}

你可能感兴趣的:(笔记,java,java-ee,开发语言)