JAVA入门

JAVA

1.JAVA概述

1.什么是编程语言?

人与计算机沟通交流的一种特殊语言

詹姆斯·高斯林 java语言创始者

配置环境变量:

jdk java开发工具包

jre java运行时环境

jvm java虚拟机

java程序执行过程:

​ 1.先编写java源代码 javac.exe

​ 2.对java源代码进行编译 javac 源文件名称

​ 编译完成后会生成当前源文件所生成的字节码文件.class

​ 字节码文件是开发人员看不懂的, 只有jvm虚拟机才能识别

​ 3.运行java代码 java.exe

​ java 类名

java语言是支持跨平台的:用java编写的源代码可以在不同的系统 上运行

使用JDK开发完成的java程序,交给JRE去运行

2.java入门

java语法规范

1.类和接口要声名一个标识符用大驼峰命名法

2.变量和方法要声名标识符用小驼峰命名法

注释:

#快捷键 ctrl + shift + /

单行注释://注释文字

多行注释:/* 注释文字 */

文档注释:/** 注释文字 */

class Hello{
    public static void main(String[] args){
        System.out.println("Hello world");
    }
}
//java最基本的Hello world

常量

在程序执行的过程中其值不可以发生改变

在方法外面定义,全大写,单词与单词之间用下划线分隔开

java是一门强类型语言,对于每一种数据都定义了明确的具体数据类型

在内存总分配了不同大小的内存空间

数据类型

1.基本数据类型:

​ a.数值型:整数类型(byte,short,int,long

​ 浮点类型(float,double

​ b.字符型:(char)

​ c.布尔型:(boolean)

2.引用数据类型:

​ a.类: (class

​ b.接口:(interface

​ c.数组: ([ ]

double d = 3.14;

float f = 3.14f;

char c = 'q';

String s = "hello world";
//String是引用数据类型

boolean b = false;

有几个数据类型关键字就开辟几份内存空间

强制转换:

double d =3.14;
int i = (int)d;

运算符+ - * / %(取余)

num++;
//后置++,先取值再赋值
--num;
//前置--,先赋值再取值
d1 += d2 -> d1 = d1 + d2;
"Hello" instanceof String //结果true,检查是否是类的对象
 
 //与运算 两者同时为真结果为真
 if (1 > 2 & 1 == 2);
 //不短路 false & false
 if (1 > 2 && 1 == 2);
 //短路运算 false && 不运算

//或运算 两者只要有一者为真,结果为真
if (1 < 2 | 2 < 3);
if (1 <2 || 2 < 3);
//非运算,取反
(1 > 2) -> false !(1 > 2) -> true
  

变量

成员变量:实例变量,静态变量

在JVM虚拟机的栈内存开辟空间,但静态变量在方法区空间开辟内存

局部变量:方法体内部的变量(包括参数中的变量)

JAVA对类的定义:

java本身是面向对象的语言,如果需要使用java去开发一个软件**

那么,软件中的每一个模块都是一个个体,每一个个体会拆分为很多单元,每个单元本身

是一个模糊的概念,针对这样的一个概念,我们一般用类去定义,然后在具体执行的时候,

需要让模块之间相互协作,正常的去模拟现实生活中场景的时候,就需要创建这个类

所对应的实例

将鼠标光标放置于未被导入的包名称上,按住alt + Enter 可以快速import包 (IntelliJ IDEA支持)

创建类

new关键字 创建类的实例

创建类实例的对象

对象.调用方法

方法

方法是完成特定功能的代码块

格式:

        修饰符  返回值类型  方法名(参数类型  参数名1, 参数类型  参数名2..){
    函数体;
    return 返回值;
}


    
package com.tly.test;

/**
 * 第一个简单java小程序,输入三个浮点数,求最小值 
 */

import java.util.Scanner;

public class Test02 {
    public static void main(String[] args) {
        
        //创建Test02的对象
        Test02 getMinNum = new Test02();
        
        //接收输入的浮点数据
        Scanner scanner = new Scanner(System.in);

        System.out.println("请输入第一个浮点数据类型:");
        
        //创建一个浮点型变量接收返回的参数
        double num1 = scanner.nextDouble();

        System.out.println("请输入第二个浮点数据类型:");

        double num2 = scanner.nextDouble();

        System.out.println("请输入第三个浮点数据类型:");

        double num3 = scanner.nextDouble();
        
        //调用对象的方法并传输传输
        double numMin = getMinNum.getMinNum(num1, num2, num3);

        System.out.println("最小值为:"+numMin);

        }
    
    //创建类的getMinNum方法
    public double getMinNum(double num1,double num2, double num3){

        double[] doubles = new double[3];

        //将数据传输给列表
        doubles[0] = num1;
        doubles[1] = num2;
        doubles[2] = num3;

        //创建中间值
        double numMin = num1;

        for (int i = 0; i < doubles.length; i++) {

            if(doubles[i] < numMin){

                numMin = doubles[i];

            }
            
        }

        return numMin;
    }
}

方法特性:

递归:

使用递归会严重损耗系统资源,因此要尽量避免使用递归

方法的重载:

在一个区间内,java语法中运行方法名称相同,参数列表不同的语法格式,这种现象我们称之为重载

数组

new创建的容器存放在堆内存中

数组是一个引用数据类型

可以通过new的方式去创建数组

在不赋值的情况下,取到的是默认值,整型默认值是0

//创建数组
int[] ints = new int[10];

//给数组赋值
ints[0] = 100;

//遍历数组
for (int i = 0; i < ints.length; i++){
    
    System.out.println(ints[i]);

}

//创建字符串数组
String[] strings = new String[5];

//赋值
String[0] = "hello";

//foreach遍历
//for (数据的数据类型 遍历的每一项 : 容器对象)
for (String str : strings){
    
     System.out.println(str);
    
}

数组操作常见的小问题:

数组索引越界

    -ArrayIndexOutOfBoundsException

    -访问到了数组中的不存在的索引时发生。

空指针异常
    -NullPointerException
    -数组引用没有指向实体,却在操作实体中的元素时。

字符串

字符串常用函数

//定义字符串
String str = "hello world";

//str.equals() 判断字符是否完全匹配 
System.out.println(str.equals("hello world"));
//输出true

//str.contains()判断是否包含某一字符串
System.out.println(str.contains("hello"));
//输出true

//str.substring()切割字符串 int beginIndex
System.out.println(str.substring(6));
//输出world 切割[0,6)

System.out.println(str.substring(6,11));
//输出world 切割(6,11]

//str.toLowerCase()将大写字母转成小写
System.out.println(str.toLowerCase());

//str.length() 获取字符串字符个数
System.out.println(str.length());
//输出11

//str.endsWith()判断字符串是否以某个字符串结尾
System.out.println(str.endsWith("d"));
//输出true

//str.split()字符串的分割
String[] strings = str.split(" ");

//遍历数组
for (String s:strings){
    
    System.out.println(s);
    
}
//输出hello 
//输出world

//将字符串转为字符数组
char[] chars = str.toCharArray();

for (String c:chars){
    
    System.out.println(c);
    
}

//str.replace()字符串替换方法
String str4 = str.replace("world","java");
System.out.println(str4);
//输出hello java

3.面向对象(万物皆对象)

面向对象特征:

  • 封装(encapsulation)

  • 继承(inheritance)

  • 多态(polymorphism)

类与对象关系

以后我们开发项目的时候,要不断的去声明类,在类中需要去封装属性和行为,

但是针对某一些属性,我们需要将它保护起来,不希望直接对外界提供访问。

所以我们会将这些属性用private关键字进行修饰,private代表的是私有的访问权限修饰符。

被private修饰的属性只能在本类中才能访问到

alt + INS键快速选择创建类的get方法与set方法

public class User {

    private String userName;

    private String address;

    private String idCard;

    private String phoneID;
    
    //提供一个idCard对应的get方法,没有参数,但是有返回值
    public String getIdCard(){

        return idCard;

    }

    //提供一个idCard对应的set方法,有参数,但是没有返回值
    public void setIdCard(String idCard){

        this.idCard = idCard;

    }


}

JVM内存分配

栈内存:主函数,定义的普通函数,基本数据类型,数据地址(引用数据类型的地址),所有的方法

堆内存:new出来的对象,应用数据类型的值

方法区内存:方法区内存中一般存放的是代码片段(java源文件编译之后的字节码文件),以及常量池

静态方法存在栈内存中,静态变量存在于方法区内存中

  • 定义在类中的实例变量一定是在堆内存中开辟的空间,因为new出来的部分一定存在于堆内存中。

  • 在JVM内存中,所有的方法,无论是静态方法还是普通方法(实例方法),都是在栈中分配空间。

局部变量和实例变量的区别:

局部变量是存在于方法体内部的变量(包含了参数),局部变量如果是基本数据类型,则直接在栈中开辟空间,如果是引用数据类型,变量声明部分在栈中,实例创建部分在堆中,如果是实例遍历,所有的内存都会在堆中去开辟。

常量池:

常量池一般存储字符串常量

Java中通过 == ,如果比较的是基本数据类型,比的是值,如果比较的是引用数据类型,则比较的是内存地址

private关键字:私有的

​ 是一个权限修饰符

​ 可以修饰成员(成员变量和成员方法)

​ 被private修饰的成员只能在本类中才能访问

this关键词

​ this:代表所在类的对象的引用(方法被哪个对象调用,this就代表那个对象)

​ this.变量名 访问的是本类中的实例变量

构造方法(Constructor)

作用:给对象的数据进行初始化

方法名与类名相同,没有返回值类型,连void也没,没有具体的返回值

如果不提供构造方法,那么系统会给出默认的构造方法,如果提供了构造方法(不管是否带参数),系统将不再提供默认无参数的构造方法

构造方法可以重载

构造方法的作用

​ 无参构造:实例化对象,但是实例化对象后,所有的属性都是没有被赋值的

​ 有参构造:实例化对象,可以在创建对象时,就给实例的属性赋值

静态关键字 static

可以修饰方法,可以修饰属性,但是不能修饰类

被静态关键字修饰的属性和方法,会成为一个类级别的元素,会随着类的加载而加载

局部变量不能被static修饰

被static修饰的变量我们称之为静态变量

静态变量会随着类的加载而加载,在JVM解析字节码文件时,该变量的内存空间就会被开辟出来

静态变量的内存空间会被开辟在该方法区中

静态方法

public class StaticTest01{
    
    private static String name; //静态变量
    
    public static void main(String[] args){
        
        //调用静态方法
        StaticTest01.getMethod();
        
        StaticTest01 staticTest01 = new StaticTest01();
        
        //调用实例方法
        staticTest01.getMethod02();
    }
    
    //声明一个静态方法
    public static void getMethod(){
        
        System.out.println("我是一个静态方法!");
        
    }
    
    public void getMethod02(){
        
        System.out.println("我是一个静态方法!");
        
    }     
    
}

静态代码块:

在类加载的时候将会被执行的语句体,而且只加载一次(在主函数执行前执行)

static{
    
    System.out.println("static 执行了!!");
    
}
一个类基本类的标准代码写法
  • 成员变量
  • 构造方法
  • ​ 无参构造方法
  • ​ 带参构造方法
  • 成员方法
  • ​ getXxx()
  • ​ setXxx()
  • 给成员变量赋值的方法
  • ​ 无参构造方法+setXxx()
  • ​ 带参构造方法

继承

概述:多个类中存在相同的属性和行为时,将这些内容抽取到单独一个类中,那么多个类无需再定义这些属性和行为,只要继承那个类即可。

通过extends关键词可以实现类与类的继承

class 子类名 extends 父类名 {}

单独的这个类称为父类,基类,或者超类;这多个类可以称为子类或者派生类

继承可以让子类去继承父类,这样子类会继承父类所有的属性和方法,在java中是不允许有多继承

Object类:是java中的祖宗类,当一个类在代码层面没有去继承任何的父类,那么这个类

默认会继承Object类

继承的重写(覆盖):

子类继承父类以后,可以获取到父类所有的方法(包含被private修饰的私有方法,但是访问不到)

如果父类方法的功能不能够满足子类的需求,这个时候,子类可以针对这个方法进行重写

父类中私有方法不能被重写

子类重写父类方法时,访问权限不能更低

父类静态方法,子类也必须通过静态方法进行重写

继承的好处:

提高了代码的复用性

  • 多个相同的成员可以放到同一个类中

提高了代码的维护性

  • 如果功能的代码需要修改,修改一处即可

让类与类之间产生了关系,是多态的前提

  • 其实这也是继承的一个弊端:类的耦合性很强

不要为了部分功能而去继承继承中类之间的体现是“is a”的关系

继承中成员变量的关系

在子类方法中访问一个变量:

  • 首先在子类局部范围找
  • 然后在子类成员范围找
  • 最后在父类成员范围找(不能访问父类局部范围)
  • 如果还是没有就报错

Super关键字

子类继承父类之后,子类可以通过super关键字去指向父类的元素(属性,普通方法,构造方法)

如果指向父类的构造用super() 如果指向子类的构造用 this()

public class Student extends Person{
    
    public Student(){
        
        //指向的是父类的无参构造,不写则默认存在
        super();
        
    }
    
    @Override
    public void sleep(){
        
        //指向父类的普通方法,只能存在于子类构造中 
        super.sleep();
        
    }
    
    public void getMethod(){
        
        //this指向当前类实例的属性
        System.out.println(this.name);
        
        //super指向父类的属性
        System.out.println(super.name);
        
    }
    
}

继承中的构造方法的关系

子类中所有的构造方法默认都会访问父类中的空参数的构造方法

​ 因为子类会继承父类中的数据,可能还会使用父类的数据

​ 所以,子类初始化之前,一定要先完成父类数据的初始化

​ 每一个构造方法的第一条语句默认都是:super()

方法重写与方法重载的区别?方法重载能改变返回值类型吗?

答:方法重写建立在类的继承关系上,是对父类同名方法的覆盖。方法重载是一个类中,多个同名方法,但是参数列表不同。方法重载可以改变返回值类型。

多态

某一事物,在不同时刻表现出来的不同状态

多态是面向对象特征之一,也是最重要的一个特征

多态思想:

为了解除类与类之间的耦合(模块与模块之间的耦合),不能面向具体编程,而要面向抽象编程(面向接口编程)

首先多态必须要有父子关系的体现(继承,实现),是在不同时刻展现出不同的状态

父类的引用接收子类对象(向上转型),或者是向下转型都是多态的体现

多态在程序执行时分为编译阶段和运行阶段:

普通方法:

编译阶段看左边:编译时要检查调用的方法在父类中是否存在

运行阶段看右边:因为我们实际new出来的对象是子类对象,所以最终执行的方法也是子类的方法

静态方法:

静态方法可以用类名去调用,类名.静态方法名()

静态方法也可以用实例去调用,对象引用.静态方法名(),本质也是通过类名去调用

静态方法是一个类级别的方法,所以它已经脱离了实例,和对象没有关系

编译看左边,运行看左边

class PloyTest01{
    
    public static void main(String[] args){
        
        Cat cat = new Cat();
        
        //通过多态机制来创建猫的实例
        Animal animal = new Cat();
        
        animal.eat();
        
        //引用数据类型的强转就是多态的一种,向上转型
        Animal animal1 = (Animal)cat;
        
        //面试题:
        Animal animal = new Rabbit();
        
        //判断animal1是否属于Cat,属于则进行强转
        if (animal1 instanceof Cat){
            
            //向下转型
            Cat cat1 = (Cat)animal1;
        
            cat1.eat();
            
        }
        
        
        
        
    }
    
}

多态前提和体现

​ 有继承关系

​ 有方法重写

​ 有父类引用指向子类对象

多态在开发中的作用

主任喂养宠物的案例:

​ 有一个主人是master喜欢喂养宠物,喂养的宠物有很多种(猫,狗,兔子,鸟),然后随着时间的推 移,主人会喂养越来越多的宠物,针对于这样的场景我们通过java程序去模拟一下(体现出多态)

​ 重载算不算多态?不算多态(没有体现父子关系)

结论:如果按照传统的方式进行开发(不使用多态):会发现程序进行迭代的时候,需要去修改主人类(需要在原有的代码继承上进行修改),这样做违背了设计模式中的开辟原则(OCP 对扩展开发,对修改关闭):在写程序的时候,尽量避免在原有的代码之上进行修改,这样的话修改的部分容易对别的类(或者说别的模块造成影响)

public class Master {

    //这是一个喂养的方法,用父类的引用去接收子类的对象
    public void feed(Pet pet){

        pet.eat();

    }

}

//创建抽象类Pet
public class Pet {

    public void eat(){

        System.out.println("所有的宠物都有吃的特性");


    }

}


public class Cat extends Pet{

    public void eat(){

        System.out.println("猫吃鱼");

    }

}

public class Rabbit extends Pet{

    public void eat(){

        System.out.println("兔子爱吃萝卜");

    }

}

public class Dog extends Pet{

    public void eat(){

        System.out.println("狗爱啃骨头");

    }

}

public class MasterTest {

    public static void main(String[] args) {


        Master master = new Master();

        master.feed(new Cat());

    }

}

抽象类

一个没有方法体的方法应该定义为抽象方法,而类中如果有抽象方法,该类必须定义为抽象类

抽象类是java中最顶级的一种表现形式,接下来就是类,再接下来就是对象

抽象类用abstract修饰 > 类class > new 对象

我们如果想创建对象的话,只能通过类class去创建,抽象类是不能创建对象的

因为抽象类的下一级是类

格式:abstract class 类名{}

  • 抽象类指的是class(本身就是抽象的事物)往上继续抽象就得到了抽象类
  • 抽象类中可以有普通方法,也可以有抽象方法
  • 抽象方法一定存在于抽象类中
  • 抽象类是不能实例化对象的,因为抽象类的下一级是类class
  • 抽象类不能实例化对象,他的作用是用来被子类继承的
  • 抽象类中可以定义普通的成员变量、静态变量、静态方法
  • 抽象方法是没有方法体的,不用提供具体的实现,当子类去继承了抽象类之后,抽象类中的所有抽象方法必须要在子类中重写
public abstract class Animal{
    
    private String name;
    
    private static int age;
    
    //定义抽象方法,抽象方法是没有方法体的
    //为什么抽象类没有方法体?因为抽象方法本身就是模糊的,不用去提供具体的实现
    public abstract String eat();
    
    public void move(){
        
        System.out.println("动物会移动")
        
    }
    
    
}

public class Cat extends Animal{
    
    @Override
    public String eat(){
        
        return "猫喜欢吃鱼";
        
    }
    
}

public  class AbstractTest01{
    
    public static void main(String[] args){
        
        Animal animal = new Cat();
        
        System.out.println(animal.eat());
        
    }
    
}

抽象类中是有构造方法的(抽象类不可以实例化对象,但是却存在构造函数)

抽象类父类构造函数是通过子类构造去调用的,通过super关键字调用父类构造

public class Cat extends Animal{
    
    
    public Cat(String name,int age){
        
        super(name,age);
        
    }
    
}

final关键字

final关键字:可以修饰类,成员变量,局部变量,方法(静态方法,实例方法)

  • 被final修饰的类不能用来被继承

  • 被final修饰的属性,如果是基本数据类型,一定在定义变量的时候就初始化,如果是引用数据类型,在程序运行

    过程中,变量的引用不能指向别的内存地址

  • 被final修饰的方法在子类继承父类之后,不能被重写

用final关键字能不能修饰抽象类?不可以,因为抽象类的作用就是用来被子类继承的,但是被final修饰的类是无法被子类继承的,final和abstract是不能同时出现的

如何去声明一个常量?

​ 常量是程序运行过程中不可以发生改变的量

​ 对于那些无论创建多少次对象,值都亘古不变的数据,我们把该属性定义为常量

public static final 属性类型 常量名称(所有字母都大写,单词之间用_分隔) = 值

​ 用static修饰常量是因为想让改常量变成一个类级别的,然后随着类加载,该常量就初始化

​ 避免多次开辟空间,浪费系统资源

​ 用final修饰常量,是因为不希望在程序运行时,该量的值发生变化,final代表最终的

接口

接口:接口是一种规范,在进行程序设计的时候,都是基于接口进行开发

因为在声明方法的时候,我们需要这个方法存在多变性,而且可以随着业务的改变,针对这个接口做具体的实现

构造方法:没有,因为接口主要是扩展功能的,而没有具体存在

接口的定义

  • 接口其实就是一个特殊的抽象类,接口中的方法只能有抽象方法
  • 通过interface来定义接口
  • 接口也是一种引用数据类型,在类加载的时候会生成字节码文件

格式: interface 接口名 {}

package com.os467.interface_test;

//声明一个接口 
public interface MyInterface01{
    
    int I = 100;
    
    Object O = new Object();
    
    static String ADDRESS = "南京"
    //以上都是常量,忽略了public static final,不是变量
        
    public static final String NAME = "Tom";
    //可以省略public static final
    
    //抽象方法
     //public abstract void getMethod();接口中下面的代码等同于这行代码 
    void getMethod();

    
}
  • 接口中的成员:抽象方法,常量
  • 由于接口中所有的方法都是抽象的,所以关键字public abstract 是可以省略掉的
  • 在接口中是允许有多继承的
  • 接口不能实例化对象,它的作用是用来被子类实现的

接口在开发中的作用:

​ 设计模式中提到在设计程序时,我们要面向接口开发,接口+多态,可以实现程序的可拓展性,灵活性

implements关键字:

作用:子类实现接口的关键字(类似extends关键字)

接口中定义的抽象方法,在子类实现之后一定要进行重写

格式: class 类名 implements 接口名{}

public class MyInterfaceImpl implements MyInterface01{
    
    @Override
    public void getMethod(){
        
       System.out.println("子类实现接口之后重写的方法") 
       
        
    }
    
}

public class InterfaceTest01{
    
    public static void main(String[] args){
        
        //通过多态的方式来创建实例
        MyInterface01 myInterface01 = new MyInterfaceImpl();
        
        myInterface01.getMethod();
        
    }
    
}

接口案例

package com.os467.interfaceTest.person;

public interface Message {

    void getMessage();

}
--------------------------------------------------------
package com.os467.interfaceTest.person;

public class QQ implements Message{

    @Override
    public void getMessage() {
        System.out.println("接收的是QQ消息");
    }
}
--------------------------------------------------------
package com.os467.interfaceTest.person;

public class Email implements Message{

    @Override
    public void getMessage() {

        System.out.println("接收的是电子邮件");
    }
}
---------------------------------------------------------
    package com.os467.interfaceTest.person;

public class QQ implements Message{

    @Override
    public void getMessage() {
        System.out.println("接收的是QQ消息");
    }
}
---------------------------------------------------------
    package com.os467.interfaceTest.person;

public class Person {

    //接收消息的方法
    public void receive(Message message){

        //调用接收消息的方法
        message.getMessage();

    }

}
----------------------------------------------------------
    package com.os467.interfaceTest.person;

public class Test {

    public static void main(String[] args) {

        Person person = new Person();

        //调用接收消息的方法
        person.receive(new QQ());
        person.receive(new WeChat());
        person.receive(new Email());

    }

}

按照多态的方式,由具体的子类实例化,这其实也是多态的一种,接口多态

类对多个接口方法的实现

//一个类的多接口实现,接口与接口之间用逗号隔开
public class MyImpl implements MyInterface01, Myinterface02{
    @Override
    public void getMethod(){
        
    }
    
    @Override
    public String getMethod02(){
        
        return null;
    }
}

接口与接口之间的多继承

//继承接口02,接口01的所有抽象方法与常量
public interface MyInterface03 extends MyInterface02, MyInterface01(){
    
}

总结:抽象类与接口的区别:

抽象类有成员变量,以及抽象方法(没有方法体)与普通方法,拥有自己的构造函数,继承抽象类的子类需要重写抽象类中的所有抽象方法,抽象类没有实例对象,因为其下一级是类

抽象类被继承体现的是”is a“的关系,共性功能

接口可以说是特殊的抽象类,只是接口内只有抽象方法和常量,接口不具有构造函数,实现接口的类需要重写接口中的所有抽象方法,接口没有实例对象,它的作用是用来被子类实现的

接口被实现体现的是:”like a“的关系,扩展功能

类与类,类与接口,接口与接口之间的关系

类与类:

​ 继承关系,只能单继承,但是可以多层继承

类与接口:

​ 实现关系,可以单实现,也可以多实现,还可以在继承一个类的同时实现多个接口

接口与接口之间:

​ 继承关系,可以单继承,也可以多继承

接口和抽象类都可以作为一个方法的返回值,参数,也可以作为类的成员变量

 public static void main(String[] args) {

        public Myinterface02 getFun(){
            
            //返回的是一个接口
            return new MyImpl();

        }

package只能存在工程的第一行

包其实就是文件夹

作用:对类进行分类管理

格式:package 包名;

多级包用”.“分开即可

访问权限修饰符区别

访问权限修饰符 本类 同一包下 子类(同一包下) 子类(不同包下) 不同包下非子类
private × × × ×
default(不写修饰符) × ×
protected ×
public

内部类

把类定义在其他类的内部,这个类就被称为内部类

内部类的访问特点:

​ 内部类可以直接访问外部类的成员,包括私有

​ 外部类要访问内部类的成员,必须创建对象

//创建内部类的实例,B是类A的内部类
A.B b = new A().new B();

//调用内部类中实例的方法
b.getMethod();

内部类中不允许写静态方法

匿名内部类

就是内部类的简化写法

前提:存在一个类或接口

​ 这里的类可以是具体类也可以是抽象类

格式:

​ new 类名或者接口名( ){重写方法;}

本质:

创建一个父类所对应的匿名对象

是一个继承了

类或者实现了接口的子类匿名对象

//给接口创建一个匿名对象,可以把 new MyInterface(){}视为接口的匿名子类
MyInterface myInterface = new MyInterface(){
    
    @Override
    public double getSum(double num1, double num2){
        
        return num1 + num2;
    }
    
};

double sum = myInterface.getSum(100,200);

System.out.println("结果为"+sum);
//结果为 300.0

4.异常

当程序出现异常时,java会创建对应的异常对象,并且抛出(throw)

当异常抛出之后,会影响接下来代码的执行(打断)

Throwable类

在 Java 中,所有的异常都有一个共同的祖先类Throwable(可抛出)

Throwable有两个子类 Error 和 Exception

Error类

错误(Error): JVM系统内部错误,资源耗尽等严重情况

Error是Throwable的子类,用于指示合理的应用程序不应该试图捕获的严重问题

无法从代码层面解决此异常

Exception类

异常(Exception): 其它因编程错误或偶然的外在因素导致的一般性问题

Exception(异常):是程序本身可以处理的异常,Exception 类有一个重要的子类RuntimeException

常见异常:

RuntimeException

ArithmeticException:数学计算异常

NullPointerException:空指针异常

NegativeArraySizeException:数组索引越界异常

ClassNotFoundException:类文件未找到异常

ClassCastException:造型异常

IOException

FileNotFoundException:文件未找到异常

EOFException:读写文件尾异常

异常:

编译时异常:当JVM进行编译的时候就会出现报错,对于那些大概率会发生的异常,我们会定义成编译时异常,编译时异常要求开发人员必须对其进行处理

直接或间接继承了Exception类的异常就是编译时异常

运行时异常:程序在运行时,才会出现的异常,小概率会出现的异常,我们会定义成运行时异常,开发人员可以不用对其进行处理

直接或者是间接的继承了**RuntimeException**类,该异常就是运行时异常

异常处理机制

1.通过throws关键字将异常往上抛

//throws关键字为方法添加方法体内可能出现的异常,如文件找不到异常,不添加则无法运行方法体中的代码
public static void main(String[] args) throws FileNotFoundException {
        
        FileInputStream fileInputStream = new FileInputStream("D:\\test\\test.txt");

        System.out.println(fileInputStream);


    }

2.通过异常的捕获try{}catch{}语句块

异常处理的try - catch - finally语句

语句块机制:

try{
    //可能会抛出特定异常的代码段
}catch(MyExceptionType myException){
    //如果myException被抛出,则执行这段代码
}catch(Exception otherException){
    //如果另外的异常otherException被抛出,则执行这段代码
}finally{
    //用于资源回收,回收的资源一般是指会造成对系统资源浪费的对象
    //无条件执行的语句!!一定会执行
} 
/*
通过这种方式去处理异常,在捕获到异常之后,还是一样会创建该异常对象,但是该对象会
被catch语句块所捕获,具体用异常类来接收该异常对象,然后在catch语句块中可以针对
该异常进行相关的处理,程序会继续往下执行
*/

注意:用try{…}catch{…}捕捉了异常之后一定要对在catch{…}中对其进行处理,那怕是最简单的一句输出语句,或打印日志来显示整个调用流程

catch(Exception e){
    
    //栈输入,打印日志
    e.printStackTrace();
    
}

throws关键字

throws : 是方法可能抛出异常的声明

用在声明方法时,表示该方法可能要抛出异常,交给上层调用它的方法程序处理

通过throws关键字(写在方法后面的)把异常向上抛(抛给该方法的调用处)

不会影响程序正常运行

//throws后面为方法中可能出现的异常,如空指针异常
public void getMethod01() throws NullPointerException{

}

//抛出编译时异常
public FileInputStream getMethod02() throws FileNotFoundException{
    
    return new FileInputStream("D:\\file\\demo.java")
    
}

自定义异常

java给我们提供了很多种异常,但是在做项目的时候,这些异常是不够用的,所以需要我们自定义异常

自定义异常方式:

  1. 直接声明一个类
  2. 如果想定义成编译时异常让该类去继承Exception
  3. 如果想定义成运行时异常,让该类去继承**RuntimeException**
  4. 然后需要提供一个构造,通过super关键字,把异常信息告诉父类即可
public class ExceptionDemmo extends Exception{
    
    //提供一个构造,把异常信息告诉父类
    public ExceptionDemmo(String message){

        super(message);

    }

}

public class RuntimeExceptionDemmo extends RuntimeException{
    
    //提供一个构造,把异常信息告诉父类
    public RuntimeExceptionDemmo(String message){

        super(message);

    }

}

throw关键字

系统在遇到异常时会自动生成一个 throw new 异常类(); 来抛出异常

throw是语句抛出一个异常,一般是在方法体的内部,当程序出现某种逻辑错误时由程序员主动抛出某种特定类型的异常

public static void main(String[] args) throws Exception{
    
    ExceptionDemo exceptionDemo = new ExceptionDemo("这是我提供的自定义异常信息");
    
    throw exceptionDemo;
    
    //也可写成throw new ExceptionDemmo("这是我提供的自定义异常信息");
}

public static void main(String[] args){
    
    RuntimeExceptionDemo runtimeExceptionDemo = new RuntimeExceptionDemo("这是我提供的自定义异常信息");
    
    throw exceptionDemo;
    
    //也可写成throw new RuntimeExceptionDemmo("这是我提供的自定义异常信息");
}

字符串比较的方法:

空指针异常时以后我们写程序经常会遇见的一种异常,接收过来的引用为空,然后拿着该引用去调用方法

空指针异常的解决方案:我们要对接收过来的引用数据类型做非空校验

public boolean getEquals(String str1, String str2){
    
        boolean b = false;
    
    if (str1 == null || str == null){
        
        System.out.println("接收的引用为空")
        
    }else{
        
           b = str1.equals(str2);//new NullPointerException();
        
        return b;
        
    }
    
    return b;
    
}

5.集合

集合:

  • ​ 集合是容器,只能存放引用数据类型
  • ​ 每个集合的实现类底层都封装了一种数据结构
  • ​ 每个集合都可以去获取迭代器接口提供的迭代方法

JAVA中的自动装箱和自动拆箱:

​ java中所有的基本数据类型都有对应的封装类(引用数据类型),我们学习的集合是只能存放引用数据类型的容器。

int i = 100;

//创建一个集合对象
ArrayList arrayList = new ArrayList();

//添加元素的方法,自动装箱:int ---> Integer
arrayList.add(i);

for (int j = 0; j < arrayList.size(); j++){
    
    //根据下标来取元素
    System.out.println(arrayList.get(j));
    
}

Java中有自动装箱机制,会把基本类型转化为其对应的封装类:

  • int → Integer
  • double → Double
  • float → Float
  • char → Character
  • byte → Byte
  • short → Short
  • long → Long
  • boolean → Boolean

将引用数据类型转为基本数据类型就是自动拆箱

Collection

Iterator接口:Collection继承的超级接口, 只有一个返回迭代器的方法

Collection : 集合层次中的根接口,JDK没有提供这个接口直接的实现类

​ 将一组对象以集合元素的形式组织到一起,在其子接口中分别实现不同的组织方法

//创建一个集合对象
Collection collection = new ArrayList();

//添加的是基本数据类型 会自动装箱
collection.add(1000);

//添加浮点型 自动装箱
collection.add(3.14);

//添加引用数据类型
collection.add(new Object());

System.out.println("集合的长度为"+collection.size());

//遍历Collection接口方法 遍历集合中的元素
//获取迭代器对象方法
Iterator iterator = collection.iterator();
while (iterator.hasNext()){
    
    Object next = iterator.next();
    
    System.out.println(next);
    
}

//判断集合中是否包含指定元素
System.out.println("是否包含元素?"collection.contains(1000));

//比较集合是否相等
System.out.println(collection.equals(collection1));

//返回哈希码值
System.out.println("hash码值为"+collection.hashCode());

//判断集合是否为空
System.out.println("集合是否为空?"+collection.isEmpty());

//根据指定元素,删除集合中所对应的元素
collection.remove(1000);

//返回集合元素个数
System.out.println("集合元素个数:"+collection.size());

//将集合转成数组
Object[] objects = collection.toArray();

for (Object object : objects){
    
    System.out.println(object)
    
}

//清空集合中的元素
collection.clear();

Collection的两个子接口:

1.List接口:

有序集合的三种访问方式

是一个有序的集合,可以包含重复的元素,提供了按索引访问的方式

public class CollectionTest01 {

    public static void main(String[] args) {

        List list1 = new ArrayList();

        List list2 = new Vector();

        list1.add(100);
        list1.add(100);
        list1.add("hello");
        list1.add(new Object());
        list1.add(list2);
        
        //List接口提供按索引访问的方法
        for (int i = 0; i < list1.size(); i++) {

            System.out.println(list1.get(i));

        }

    }

}

迭代器方法访问元素:

Iterator接口定义了对Collection类型对象中所含元素的遍历等增强处理功能

​ 可以通过Collection接口中定义的iterator()方法

​ 获得一个对应的Iterator(实现类)对象

​ Set(实现类)对象对应的Iterator仍然是无序的

​ List(实现类)对象对应的ListIterator对象可以实现对所含元素的双向遍历:

​ 使用next()方法和previous()方法

  //获取迭代器对象,注意:获取迭代器对象之后不允许对集合元素进行修改或删除,否则会报错
        Iterator iterator = list1.iterator();

        /*遍历集合
        iterator.hasNext()是一个指针,判断集合中下一个
        单元中是否存在元素,如果存在指针向下走一个单元*/
        while(iterator.hasNext()){

            //返回当前指针指向的元素
            Object next = iterator.next();
            
            //迭代器的删除方法,删除的是当前遍历的元素
            //iterator.remove();
            
            //最好不要在迭代器中用
            //List.remove(next)

            System.out.println(next);
        }

forEach循环(加强版for循环):

for (Object obj : list1){

            System.out.println(obj);
            
        }

案例分析(关于集合当中的方法):

List的方法:

contains 方法: 底层重写了equals方法,判断集合中是否包含某一个元素,如果该元素重写了equals方法,比较的就是内容,如果没有重写equals方法,默认会采用Object类中的equals方法,这时候比较的就是地址

remove方法: 底层重写了equals方法,删除集合中的元素,但是永远只能删除一个元素,因为在底层删除之后就return了

/**
 * 创建两个学生对象(属性有 姓名,年龄,身份证号),然后
 * 重写equals方法,在年龄姓名身份证号相同的清空下,两个比较的实例才是相同的!
 *
 */
public class ListDemo {
    public static void main(String[] args) {

        //创建集合对象
        List list = new ArrayList();

        //创建两个学生实例
        Student student1 = new Student("Tom",20,"1232312312");
        Student student2 = new Student("Tom",20,"1232312312");

        list.add(student1);
        
        //因为在contains的底层调用了equals方法,student在contains中用Object类接收
        //而父类Object中的equals方法不能满足需求,因此需要在Student类中去重写equals方法
        System.out.println(list.contains(student2));
    } 

}
public class Student {

    private String name;

    private int age;

    private String idCard;

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

    public Student() {
    }

    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 String getIdCard() {
        return idCard;
    }

    public void setIdCard(String idCard) {
        this.idCard = idCard;
    }

    //重写equals方法
    @Override
    public boolean equals(Object obj) {
        
        //如果接收到为空 或者 obj不是Student类的对象 返回false
        if (obj == null || !(obj instanceof  Student)){

            return false;

        }
        
        //对obj向下转型,用Student类的对象student接收
        Student student = (Student)obj;
        
        //满足 对象与student的姓名,年龄,身份证号相等 返回true
        if(this.name.equals(student.name) && this.age == student.age && this.idCard.equals(student.idCard)){

            return  true;

        }
        
        //不满足则返回false
        return false;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", idCard='" + idCard + '\'' +
                '}';
    }
}

ArrayListvector 集合区别:

这两个集合底层都封装了数组这一种数据结构,ArrayList是线程不安全的,Vector是线程安全的

所有封装的方法都是一样的,还有就是扩容机制有一点不一样,ArrayListvector最开始的时候只能存放10个元素,默认初始容量是10,当元素超过10个之后,就会触发底层的扩容机制

ArrayList的扩容因子是1.5 10 * 1.5 = 15

Vector的扩容因子是2.0 10 * 2.0 = 20

ArrayListLinkedList 集合区别:

ArrayList 底层是数组,LinkedList 底层是链表结构

数组查询效率高,随机增删效率低

链表随机增删效率高,查询效率低

集合的泛型

JDK1.5版本之后引入的新特性,强制让集合只能存放某一种数据类型

<数据类型>钻石表达式

//创建只能存放字符串类型的数组 
ArrayList<String> string = new ArrayList<>();

自定义泛型:

//<>内部的类容可以是任意的,例如可以是Collection<? extends E> var1,即传入数据类型必须是Collection的子类
//<E>的存在用来接收Person类内部的E的具体引用数据类型
public class Person<E> {
    
    //e这个成员变量的类型为E,E的类型由外部指定
    private E e;
    
    //泛型构造方法形参e的类型也为E,E的类型由外部指定
    public void getE(E e){

        System.out.println(e);

    }

}

------------------------------------------------------------
public class Test {

    public static void main(String[] args) {

        Person<String> stringPerson = new Person<>();

        stringPerson.getE("abc");

    }

}

2.Set接口:

Set接口的实现类:HashSet

底层封装了HashMap

HashSet不允许出现重复元素,不保证集合中元素的顺序(存入的顺序与取出的不同

HashSet中允许包含值为null的元素,但最多只能由一个null元素

 HashSet<String> hashSet = new HashSet<>();

        hashSet.add("abc");
        hashSet.add("efg");
        hashSet.add("xyz");
        hashSet.add("xyz");

        for (String s : hashSet) {

            System.out.println(s);

        }

Set接口的实现类:TreeSet

底层封装了TreeMap

//创建TreeSet集合
TreeSet treeSet = new TreeSet();

        treeSet.add(123);
        treeSet.add(124);
        treeSet.add(120);
        treeSet.add(128);
        treeSet.add(129);

        for (Object o : treeSet) {

            System.out.println(o);

        }

Map接口:

  1. map集合是存放键值对的容器

  2. value是key的一个替代品,我们以后操作元素时,是根据key去操作

  3. map集合的key是不允许重复的,如果在添加元素时,key的值重复,那么后添加的元素将会把前一个覆盖掉

  4. value的值可以重复

​ Map:包含了key-value对, Map不能包含重复的key, SortedMap是一个按照升序排列key的Map

​ 映射(map)是一个存储关键字和值的关联或者说是关键字/值对的对象,给定一个关键字,可以得到它的值

​ 关键字和值都是对象,关键字必须是唯一的,但值是可以被复制的

​ 有些映射可以接收null关键字和null值,而有的则不行

  • 一个Map中的key值是唯一的,不重复(如,不要用员工姓名作为key)
  • 一个Map中一个key只能对应一个value(可以为空),但一个value可以有多个key与之对应
  • Map能让你通过key快速查找到相应的对象并获得它对应的value的引用(如存储员工资料并用员工ID作为key来查找某一员工的信息)
//创建集合对象
Map map = new HashMap();
Map map2 = new HashMap();

//添加元素
map.put("name","jack");
map.put("age",20);
map.put("address","北京市");
map.put("idCard","123456");

//查看集合的长度
System.out.println("集合的长度为"+map.size());

//查询集合中是否包含某一个key
System.out.println("是否包含key"+map.containsKey("name"));

//是否包含value
System.out.println("是否包含value"+map.containsValue(20));

//判断集合对象是否相等
System.out.println(map.equals(map2));

//根据key来获取value
System.out.println("name的值为"+map.get("name"));

//判断集合是否为空
System.out.println("是否为空"+map.isEmpty);

//将map集合中的key部分转成set集合
Set set = map.keySet();

for(Object o : set){
    
    System.out.println(o);
    
}

//复制集合中所有的元素,将map的内容复制给map2
map2.putAll(map);

//根据key来删除整个键值,返回的是删除的key所对应的value
Object age = map.remove("age");

//将map集合所有的value部分转成collection集合 
Collection collection = map.values();

for(Object o : collection){
    
    System.out.println(o);
    
}

如何遍历Map集合(两种方法):

1.keySet方法:将map集合所有key部分转成set集合,再根据get()获取对应value的值

//创建map集合对象
Map map = new HashMap();

//添加元素
map.put("name","Tom");
map.put("age",20);
map.put("address","北京市");
map.put("idCard","123456");

//将所有的key转成set集合
Set set = map.keySet();

//获取迭代器对象
Iterator iterator = set.iterator();

while (iterator.hasNext()){
    
    Object key = iterator.next();
    
    //根据key获取value
    Object value = map.get(key);
    
    System.out.println(key+" = "+value);
    
}

2.Set<Map.Entry<K,V> entrySet()

将map集合转成set集合(效率比第一种高)

Set<Map.Entry<String,String>> entries = map.entrySet();

//这里的Entry<k,V>为Map的内部接口
for (Map.Entry<String,String> entry : entries){
    
    //获取key的值
    String key = entry.getKey();
    
    //获取value的值
    String value = entry.getValue();
    
    System.out.println(key+" = "+value);
    
}

Map接口实现类:HashMap:

hashMap集合的存储过程:

hashCode()随机生成哈希码值

我们添加到集合的元素是引用数据类型

那一定会继承Object类中的hashCode方法

首先map集合会调用put()方法来添加元素,

这个时候hashMap底层会生成hash值(如果在该引用数据类型中重写了hashCode方法,那么生成的不再是随机值,数据便会存储在同一单元中)

根据这个hash值来计算该元素的位置应该存放在数组的哪个单元下面

确定了存放的单元,这个时候就会去拿着key的引用

与该单元下所有的node节点进行一个equals比较(该引用数据类型有没有重写equals方法如果重写了,比较的是对应属性的内容,如果没有重写,那比较的就是内存地址)

如果发下equals的返回值是true,新添加进来的节点将会把旧的节点覆盖掉

如果equals的返回值是false,该节点将会存放在数组单元中的单向链表的最下面(成为链表尾节点)

//创建集合对象
HashMap<Student,Object> hashMap = new HashMap<>();

//添加元素,key和value
hashMap.put(new Student("jack",20),100);
hashMap.put(new Student("jack",20),101);

hashMap案例:

//根据hashMap统计字符出现的个数
String str = "djaiwj jdjia1dj dj";

        char[] chars = str.toCharArray();

        //创建hashMap集合
        HashMap<Character,Integer> hashMap = new HashMap<>();

        for (char aChar : chars) {

            //在遍历字符数组的过程中判断map集合中是否有该字符,如果有在当前个数基础上加一
            //如果不存在,则把该字符重新添加到集合中,个数赋值为1
            if (hashMap.containsKey(aChar)){

                //个数在当前基础上加一
                hashMap.put(aChar,hashMap.get(aChar)+1);

            }else{
                
                hashMap.put(aChar,1);

            }

        }

        //遍历map集合
        Set<Map.Entry<Character, Integer>> entries = hashMap.entrySet();

        for (Map.Entry<Character, Integer> entry : entries) {

            System.out.println(entry.getKey()+"===>"+entry.getValue());
        }

Map接口实现类:HashTable:

HashTableHashMap的区别:

HashMap key-value可以为一次空

HashTable 不可以为空

HashTable是线程同步的,也是线程安全的

其实hashtable就是线程安全版的hashmap,所有的方法以及使用方式都是一样的

hashtable的key和value不允许为空

arrayListvector是一样的,hashmap的执行效率要比hashtable高,因为hashtable所有的方法都添加了线程同步关键字,这样的话在多线程场景下,执行效率是不高,但是以后开发中我们可以通过别的途径来解决线程安全问题,所以hashMap这个集合是我们以后使用的最多key-value集合

Hashtable<Object,Object> hashtable = new Hashtable<>();

Map接口实现类:TreeMap:

treeMap是一个可以按照key自动排序的集合

//创建集合对象
Treemap<Integer,Object> treeMap = new TreeMap<>();

treeMap.put(110,101);
treeMap.put(100,101);
treeMap.put(102,101);

//map集合转set集合
Set<Map.Entry<String,String>> entries = treeMap.entrySet();

//forEach
for (Map.Entry<Integer, Object> entry : entries){
    
    System.out.println(entry.getKey()+" = "+entry.getValue());
    
}
//输出结果:
//100 = 101
//102 = 101
//110 = 101

//对自定义的引用数据类型使用treeMap,treeSet需要在类中实现Comparatable接口
public class Goods implements Comparable<Goods> {
    
    private Double price;
    
    //重写compareTo方法,二叉树根据返回的值的正负来存放数据
@Override
public int compareTo(Goods goods){
    
    return (int)(this.price - goods.price);
   
}
    
}

案例:

hashMap统计字符出现的个数

package com.os467.hashMapDemo;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;

public class HashMapTest {

    public static void main(String[] args) {

        //统计每个字符出现的次数
        getCharNum();

    }

    /**统计字符出现的个数
     *s.matches()正则表达式方法,用于匹配符合规则的字符串
     */
    public static void getCharNum(){

        System.out.println("---------统计每个字符出现的次数----------");

        //创建HashMap集合
        HashMap<Character,Integer> hashMap = new HashMap<>();

        //将字符串切割成字符数组
        char[] chars = getString().toCharArray();

        //遍历字符串数组
        for (char c: chars) {

            if (hashMap.containsKey(c)) {

                //个数在当前基础上加一
                hashMap.put(c, hashMap.get(c) + 1);

            } else {

                hashMap.put(c, 1);

            }

            }
        //将map集合转成set集合
        Set<Map.Entry<Character, Integer>> entries = hashMap.entrySet();

        //遍历set集合
        for (Map.Entry<Character, Integer> entry : entries) {

            System.out.println(entry.getKey()+" ===> "+entry.getValue());

        }




    }

    //获取字符串的方法
    public static String getString(){

        return "adaw diicj123";

    }

}

Properties集合:

继承了hashtable

        //创建集合对象
        Properties properties = new Properties();

        //创建流对象
        FileReader fileReader = new FileReader("jdbc");

        //通过流对象去解析数据,并把数据放到集合
        properties.load(fileReader);

        //私有的方法
        properties.setProperty("username","root");
        properties.setProperty("password","root");
        properties.setProperty("url","jdbc:mysql://localhost:3306");
        properties.setProperty("driver","com.mysql.jdbc.driver");

        //取出集合中的数据
        String username = properties.getProperty("username");
        String password = properties.getProperty("password");
        String url = properties.getProperty("url");
        String driver = properties.getProperty("driver");

        System.out.println(username);
        System.out.println(password);
        System.out.println(url);
        System.out.println(driver);

集合总结

Collection体系:

​ List接口:

ArrayList:

​ 有序可重复的集合,底层封装了数组这种数据结构,所以有数组所有的特性,可以通过下标索引来获取元素,查询数据的效率会很高,但是随机增删的效率会比较低,本身是一个线程不安全的集合,默认初始容量是10,如果存放元素超过10会触发扩容机制,扩容因子是1.5,每触发一次扩容机制,加载为原来的1.5倍

Vector:

​ Vector就是一个线程安全版的Arraylist,这个集合的所有方法都添加了线程同步关键字,在多线程场景下,效率是很低的,我们一般不会使用这个集合,默认初始容量是10,如果存放元素超过10会触发扩容机制,扩容因子是2.0,每触发一次扩容机制,加载为原来的2倍

LinkedList:

​ 这个集合在底层封装了单向链表这种数据结构,所以该集合拥有链表的所有特性,查询效率是不高的,但是随机增删效率很高,而且在LinkedList底层还为我们封装了队列,我们可以去调用入队和出队的方法

​ Set接口:

HashSet:

​ 在底层为我们聚合了HashMap,在添加元素的时候,实际上调用了HashMap的put方法,元素存放在put方法的key部分

HashSet是一个无序不可重复的集合,存入数据的顺序和取出数据的顺序是不同的

TreeSet:

​ 在底层为我们聚合了TreeMap,在添加元素的时候,实际上调用了TreeMap的put方法,元素存放在put方法的key部分

TreeSet是一个可以按照排序规则自动排序的方法,如果存入元素的引用时我们自定义的,该引用必须要实现比较器接口Comparable<泛型>,重写比较规则,不然会报类型转换异常

Map体系:

HashMap:

​ 可以以key-value的形式去存放数据,key和value都是允许为空的,不是线程安全的集合,但是执行效率很高,在底层封装的是哈希散列

HashTable:

​ 线程安全版的HashMap,执行效率不是很高,key和value都不允许重复

TreeMap:

​ 是可以根据key来完成自动排序的key-value集合,如果存入元素的引用是我们自定义的,该引用必须要实现比较器接口Comparable<泛型>,重写比较规则,不然会报类型转换异常

Properties:

​ 这个集合继承了HashTable,所以该集合是一个线程安全的集合,而且提供了私有的方法SetProper(),getProper(),该集合可以集合流对象高效的去读取配置文件,把数据解析到集合中



转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以邮件至 1300452403@qq.com

文章标题:JAVA入门

字数:13.4k

本文作者:Os467

发布时间:2022-05-28, 00:32:07

最后更新:2022-09-05, 00:08:28

原始链接:https://os467.github.io/2022/05/28/JAVA%E5%85%A5%E9%97%A8/

版权声明: "署名-非商用-相同方式共享 4.0" 转载请保留原文链接及作者。

×

喜欢就点赞,疼爱就打赏