【基础】Java语法基础笔记

1. ArrayList

  • ArrayList集合的特点: 长度可以变化,只能存储引用数据类型。
 ArrayList<Integer> res = new ArrayList<>(); //right
 ArrayList<int> res1 = new ArrayList<int>(); //error:Type argument cannot be of primitive type
  • ArrayList的remove方法有两种方式:remove(int index)和remove(Object o)。前者是按照指定下标删除元素,后者是按照指定值删除元素。如果使用的是自定义对象,务必要保证该对象已经正确地重写了equals()方法,否则可能会无法删除指定元素。

2. static 关键字

  • 当 static 修饰成员变量时,该变量称为静态变量; static 修饰成员方法时,该方法称为静态方法该类的每个对象都共享同一个类的静态变量和静态方法。任何对象都可以更改该静态变量的值或者访问静态方法。但是不推荐这种方式去访问。因为静态变量或者静态方法直接通过类名访问即可,完全没有必要用对象去访问。
  • 无static修饰的成员变量或者成员方法,称为实例变量,实例方法,实例变量和实例方法必须创建类的对象,然后通过对象来访问。
  • static修饰的成员属于类,会存储在静态区,是随着类的加载而加载的,且只加载一次,所以只有一份,节省内存。存储于一块固定的内存区域(静态区),所以,可以直接被类名调用。它优先于对象存在,所以,可以被所有对象共享。
  • 无static修饰的成员,是属于对象,对象有多少个,他们就会出现多少份。所以必须由对象调用。

3. Override注解

  • @Override:重写注解。这个注解标记的方法,就说明这个方法必须是重写父类的方法,否则编译阶段报错。建议重写都加上这个注解,一方面可以提高代码的可读性,一方面可以防止重写出错。

4. 构造方法

  • 构造方法的名字是与类名一致的。所以子类是无法继承父类构造方法的。

  • 构造方法的作用是初始化对象成员变量数据的。所以子类的初始化过程中,必须先执行父类的初始化动作子类的构造方法中默认有一个super() ,表示调用父类的构造方法,父类成员变量初始化后,才可以给子类使用。(先有爸爸,才能有儿子)

    class Student extends Person {
        private double score;
    
        public Student() {
            //super(); // 调用父类无参构造方法,默认就存在,可以不写,必须在第一行
            System.out.println("子类无参");
        }
     
         public Student(double score) {
            //super();  // 调用父类无参构造方法,默认就存在,可以不写,必须在第一行
            this.score = score;    
            System.out.println("子类有参");
         }
    }
    

4. super 和 this 关键字

  • super代表的是父类对象的引用,this代表的是当前对象的引用。
      	this.成员变量    	-->    本类的
      	super.成员变量    	-->    父类的
      	this.成员方法名()  	-->    本类的    
      	super.成员方法名()   -->    父类的
    
      	super(...) -- 调用父类的构造方法,根据参数匹配确认
      	this(...) -- 调用本类的其他构造方法,根据参数匹配确认
    
  • 子类的每个构造方法中均有默认的super(),调用父类的空参构造。手动调用父类构造会覆盖默认的super()。
  • java支持在构造函数中调用其他的构造函数,通过this(…)实现。this(…)可以调用本类中的其他构造方法,根据参数去确定调用哪个构造方法。
  • super() 和 this() 都必须是在构造方法的第一行,所以不能同时出现。
        Student(String name) {
          super(name);
          this(name, "man"); //error:Call to 'this()' must be first statement in constructor body
      }
    
  • 静态方法中不能使用super和this关键字。因为静态方法是存储在静态区内的,静态区会随着类加载器一起加载到内存当中,这时候,只是加载到内存当中,但是并没有真正的去运行,此时也就没有产生实例化的对象。而this是表示当前对象的。super表示父类的对象,这时候,连实例化的对象都没有产生,所以this和super也就不存在。

5. 多态的定义和前提

  • 多态: 是指同一行为,具有多个不同表现形式。
  • 前提:
    • 有继承或者实现关系
    • 方法的重写【意义体现:不重写,无意义】
    • 父类引用指向子类对象【格式体现】
      父类类型:指子类对象继承的父类类型,或者实现的父接口类型。
  • 多态的运行特点
    • 调用成员变量时:编译看左边,运行看左边
    • 调用成员方法时:编译看左边,运行看右边
    Fu f = new Zi()//编译看左边的父类中有没有name这个属性,没有就报错
    //在实际运行的时候,把父类name属性的值打印出来
    System.out.println(f.name);
    
    //编译看左边的父类中有没有show这个方法,没有就报错
    //在实际运行的时候,运行的是子类中的show方法
    f.show();
    
  • 多态编译阶段是看左边父类类型的,如果子类有些独有的功能,此时多态的写法就无法访问子类独有功能了。

6. 多态的转型

  • 多态的转型分为向上转型(自动转换)与向下转型(强制转换)两种。
    • 向上转型:多态本身是子类类型向父类类型向上转换(自动转换)的过程,这个过程是默认的。 当父类引用指向一个子类对象时,便是向上转型。如:父类类型 变量名 = new 子类类型();
    • 向下转型:父类类型向子类类型向下转换的过程,这个过程是强制的。 一个已经向上转型的子类对象,将父类引用转为子类引用,可以使用强制类型转换的格式,便是向下转型。如:子类类型 变量名 = (子类类型) 父类变量名;

7. 导包

  • 什么时候需要导包?
    ​ 情况一:在使用Java中提供的非核心包中的类时
    ​ 情况二:使用自己写的其他包中的类时
  • 什么时候不需要导包
    ​ 情况一:在使用Java核心包(java.lang)中的类时
    ​ 情况二:在使用自己写的同一个包中的类时

8. 访问修饰符

  • Java提供了四种访问权限,分别是public、protected、private、默认(不加权限修饰符,就是默认权限)。使用不同的访问权限修饰符修饰时,被修饰的内容会有不同的访问权限
  • 访问修饰符修饰类
    • 除内部类之外,只能使用public和(default)来进行修饰类

      • public修饰: 用这个修饰符修饰的类可以被所有其它类进行访问,如果不同包只需要使用import导包语句将这个类导入就可以使用。
      • default修饰: 不使用修饰符的类只能被本包中的其它类进行访问,不能被不同包中的类访问。

      protected、private不能进行对类的修饰,在idea里面使用编译会报错。(如:Modifier ‘protected’ not allowed her)
      protected: 这个修饰符不能修饰类,原因就是这会破坏Java封装的原则。
      private: 这个修饰符修饰类的时候会让整个类变得没有意义,因为这不能被其它任何类所访问。

    • 对于内部类,可以被所有修饰符修饰,其作用范围和成员属性、成员方法一致。具体规则如下:

publicprotected默认private
同一类中
同一包中的类
不同包的子类
不同包中的无关类

可见,public具有最大权限。private则是最小权限

9. final 关键字

  • Java提供了final 关键字,表示修饰的内容不可变。可以用于修饰类、方法和变量。具体含义如下:
    • 类:被修饰的类,不能被继承。
    • 方法:被修饰的方法,不能被重写。
    • 变量:被修饰的变量,有且仅能被赋值一次。
  • final成员变量的初始化时机
    • 显式初始化(在定义成员变量的时候立马赋值)(常用);
    • 构造方法初始化(在构造方法中赋值一次)(不常用);
  • 被final修饰的常量名称,一般都有书写规范,所有字母都大写

10. 抽象类

  • 抽象方法:没有方法体的方法
  定义格式:修饰符 abstract 返回值类型 方法名 (参数列表);
  具体示例:public abstract void run()
  • 抽象类:用abstract修饰符修饰的类
	定义格式:修饰符 abstract class 类名字 { 
             }
	具体示例:public abstract class Animal {
                   public abstract void run()}
  • 抽象类的细节
    • 抽象类不能创建对象,如果创建,编译无法通过而报错。只能创建其非抽象子类的对象。

      理解:假设创建了抽象类的对象,调用抽象的方法,而抽象方法没有具体的方法体,没有意义。

    • 抽象类中,可以有构造方法,是供子类创建对象时,初始化父类成员使用的。

      理解:子类的构造方法中,有默认的super(),需要访问父类构造方法。

    • 抽象类中,不一定包含抽象方法,但是有抽象方法的类必定是抽象类

      理解:未包含抽象方法的抽象类,目的就是不想让调用者创建该类对象,通常用于某些特殊的类结构设计。

    • 抽象类的子类,必须重写抽象父类中所有的抽象方法,否则子类也必须定义成抽象类,编译无法通过而报错。

      理解:假设不重写所有抽象方法,则类中可能包含抽象方法。那么创建对象后,调用抽象的方法,没有意义。

    • 抽象类存在的意义是为了被子类继承。

      理解:抽象类中已经实现的是模板中确定的成员,抽象类不确定如何实现的定义成抽象方法,交给具体的子类去实现。

11. 接口

  • 接口中,只包含抽象方法和常量。
  • 接口中的抽象方法默认会自动加上public abstract修饰,无需自己手写!!​ 按照规范:以后接口中的抽象方法建议不要写上public abstract。因为没有必要啊,默认会加上。
  • 在接口中定义的成员变量默认会加上: public static final修饰。也就是说在接口中定义的成员变量实际上是一个常量。这里是使用public static final修饰后,变量值就不可被修改,并且是静态化的变量可以直接用接口名访问,所以也叫常量。常量必须要给初始值。常量命名规范建议字母全部大写,多个单词用下划线连接。
    public interface InterF {
        // 抽象方法!
        //    public abstract void run();
        void run();
    
        //    public abstract String getName();
        String getName();
    
        //    public abstract int add(int a , int b);
        int add(int a , int b);
    
        // 它的最终写法是:
        // public static final int AGE = 12 ;
        int AGE  = 12; //常量
        String SCHOOL_NAME = "程序员";		
    }
    
  • 意义:接口体现的是一种规范,接口对实现类是一种强制性的约束,要么全部完成接口声明的功能,要么自己也定义成抽象类。这正是一种强制性的规范。
  • 如果一个接口中,有10个抽象方法,但是我在实现类中,只需要用其中一个,该怎么办?

    可以在接口跟实现类中间,新建一个中间类(适配器类)
    让这个适配器类去实现接口,对接口里面的所有的方法做空重写。
    让子类继承这个适配器类,想要用到哪个方法,就重写哪个方法。
    因为中间类没有什么实际的意义,所以一般会把中间类定义为抽象的,不让外界创建对象

12. 内部类

  • 将一个类A定义在另一个类B里面,里面的那个类A就称为内部类,B则称为外部类。可以把内部类理解成寄生,外部类理解成宿主。
  • 什么时候使用内部类

    一个事物内部还有一个独立的事物,内部的事物脱离外部的事物无法独立使用
    人里面有一颗心脏。
    汽车内部有一个发动机。
    为了实现更好的封装。

  • 内部类的分类(按定义的位置来分)
    • 成员内部类,类定义在了成员位置 (类中方法外称为成员位置,无static修饰的内部类)

      外部类.内部类 变量 = new 外部类().new 内部类();
      Outer.Inner oi = new Outer().new Inner();

    • 静态内部类,类定义在了成员位置 (类中方法外称为成员位置,有static修饰的内部类)

      外部类.内部类 变量 = new 外部类.内部类构造器;
      Outer01.Inner01 in = new Outer01.Inner01(“张三”);

    • 局部内部类,类定义在方法内
          class 外部类名 {
      	数据类型 变量名;		
      	修饰符 返回值类型 方法名(参数列表) {
      		// …
      		class 内部类 {
      			// 成员变量
      			// 成员方法
      		}
      	}
      }
      
    • 匿名内部类,没有名字的内部类,可以在方法中,也可以在类中方法外。
      • 匿名内部类必须继承一个父类或者实现一个父接口
      • 匿名内部类格式
        new 父类名或者接口名(){
          // 方法重写
          @Override 
          public void method() {
              // 执行语句
          }
        
      };
  • 匿名内部类的使用方式
    • 以实现接口为例
      public interface Swim {
          public abstract void swimming();
      }
      	
      public class Demo07 {
          public static void main(String[] args) {
              // 使用匿名内部类
      		new Swim() {
      			@Override
      			public void swimming() {
      				System.out.println("自由泳...");
      			}
      		}.swimming();
      
              //接口 变量 = new 实现类(); 	        
              Swim s2 = new Swim() {
                  @Override
                  public void swimming() {
                      System.out.println("蛙泳...");
                  }
              };
              //多态,走子类的重写方法
              s2.swimming();
          }
      }
      
  • 成员内部类的细节
    • 内部类如果想要访问外部类的成员变量,外部类的变量必须用final修饰,JDK8以前必须手动写final,JDK8之后不需要手动写,JDK默认加上。
    • 内部类访问外部类对象的格式是:外部类名.this
      public class Test {
          public static void main(String[] args) {
              Outer.inner oi = new Outer().new inner();
              oi.method();
          }
      }
      
      class Outer {	// 外部类
          private int a = 30;
      
          // 在成员位置定义一个类
          class inner {
              private int a = 20;
      
              public void method() {
                  int a = 10;
                  /*请在?地方填上相应代码,以达到输出的内容*/
                  System.out.println(???);	// 10   答案:a
                  System.out.println(???);	// 20	答案:this.a
                  System.out.println(???);	// 30	答案:Outer.this.a
              }
          }
      }
      

13. 匿名内部类

  • 特点

    • 定义一个没有名字的内部类
    • 这个类实现了父类,或者父类接口
    • 匿名内部类会创建这个没有名字的类的对象
  • 使用场景

    • 通常在方法的形式参数是接口或者抽象类时,将匿名内部类作为参数传递。代码如下:
      // 定义一个方法,模拟请一些人去游泳
      public static void goSwimming(Swim s) {
          s.swimming();
      }
      	
      interface Swim {
          public abstract void swimming();
      }
      
      public class Demo07 {
          public static void main(String[] args) {
              // 普通方式传入对象
              // 创建实现类对象
              Student s = new Student();	        
              goSwimming(s);
              
              // 匿名内部类使用场景:作为方法参数传递
              Swim s3 = new Swim() {
                  @Override
                  public void swimming() {
                      System.out.println("蝶泳...");
                  }
              };
              // 传入匿名内部类
              goSwimming(s3);
      
              // 完美方案: 一步到位
              goSwimming(new Swim() {
                  public void swimming() {
                      System.out.println("大学生, 蛙泳...");
                  }
              });
      
              goSwimming(new Swim() {
                  public void swimming() {
                      System.out.println("小学生, 自由泳...");
                  }
              });
          }
      }
      

14. Object 类

  • Object 是类层次结构的根,每个类都可以将 Object 作为超类。所有类都直接或者间接的继承自该类。、
  • Object类中的常见方法
      public String toString()				//返回该对象的字符串表示形式(可以看做是对象的内存地址值)
      public boolean equals(Object obj)		//比较两个对象地址值是否相等;true表示相同,false表示不相同
      protected Object clone()    			//对象克隆(默认浅拷贝)
    
  • toString()
    • 在通过输出语句输出一个对象时,默认调用的就是toString()方法;

      // 创建学生对象
      Student s1 = new Student(“itheima” , “14”) ;
      // 直接输出对象s1
      System.out.println(s1);

    • 输出地址值一般没有意义,我们可以通过重写toString方法去输出对应的成员变量信息(快捷键:atl + insert , 空白处 右键 -> Generate -> 选择toString)
    • toString方法的作用:以良好的格式,更方便的展示对象中的属性值;
    • 一般情况下Jdk所提供的类都会重写Object类中的toString方法;
  • equals()
    • 默认情况下equals方法比较的是对象的地址值;
    • 比较对象的地址值是没有意义的,因此一般情况下我们都会重写Object类中的equals方法;
  • clone()
    • 把A对象的属性值完全拷贝给B对象,也叫对象拷贝,对象复制。
    • Object类默认的是浅拷贝;(即:基本数据类型拷贝过来的是具体的数据,引用数据类型拷贝过来的是地址值)
    • 如果类中包含引用数据类型,那么一般需要进行对象clone时,都要重写Obejct类中的clone()方式,实现为深拷贝(即:基本数据类型拷贝过来,字符串复用,引用数据类型重新创建新的);

15. Objects 类

  • Objects类是被final修饰的,因此该类不能被继承。
  • Objects类提供了一些对象常见操作的方法。比如判断对象是否相等,判断对象是否为null等等。
  • Objects类中的常见方法:
    public static String toString(Object o) 					// 获取对象的字符串表现形式
    public static boolean equals(Object a, Object b)			// 比较两个对象是否相等
    public static boolean isNull(Object obj)					// 判断对象是否为null
    public static boolean nonNull(Object obj)					// 判断对象是否不为null
    

16. 装箱与拆箱

  • 基本类型对应的包装类
基本类型对应的包装类(位于java.lang包中)
byteByte
shortShort
intInteger
longLong
floatFloat
doubleDouble
charCharacter
booleanBoolean
  • 基本类型与对应的包装类对象之间,来回转换的过程称为”装箱“与”拆箱“:
    • 装箱:从基本类型转换为对应的包装类对象。
    • 拆箱:从包装类对象转换为对应的基本类型。
      //装箱:基本数值---->包装对象
      Integer i = new Integer(4);//使用构造函数函数
      Integer iii = Integer.valueOf(4);//使用包装类中的valueOf方法
      //拆箱:包装对象---->基本数值
      int num = i.intValue();
      
  • 自动装箱与自动拆箱
    • 由于我们经常要做基本类型与包装类之间的转换,从Java 5(JDK 1.5)开始,基本类型与包装类的装箱、拆箱动作可以自动完成。例如:
      Integer i = 4;//自动装箱。相当于Integer i = Integer.valueOf(4);
      
      i = i + 5; //等号右边:将i对象转成基本数值(自动拆箱) i.intValue() + 5;
                //加法运算完成后,再次装箱,把基本数值转成对象。
      
  • 经验建议
    • 获取Integer对象的时候不要自己new,而是采取直接赋值或者静态方法valueOf的方式。因为在实际开发中,-128~127之间的数据,用的比较多。如果每次使用都是new对象,那么太浪费内存了。所以,提前把这个范围之内的每一个数据都创建好对象,如果要用到了不会创建新的,而是返回已经创建好的对象。

17. 集合

1) 数组和集合的区别

  • 数组可以存基本数据类型和引用数据类型;
  • 集合只能存引用数据类型,如果要存基本数据类型, 需要存对应的包装类;

2)集合类体系结构

  • 集合有两个基本接口:Collection和Map
    集合类体系结构

3) Collection集合(接口)

  • Collection集合是单例集合的顶层接口, 它表示一组对象, 这些对象也称为Collection的元素;JDK 不提供此接口的任何直接实现,它提供更具体的子接口(如Set和List)实现。
  • Collection集合的遍历(3种)
    • 迭代器遍历

      • 1.迭代器遍历完毕,指针不会复位
      • 2.当循环结束之后,迭代器的指针已经指向了最后没有元素的位置,如果继续使用next() 报错NoSuchElementException
      • 3.循环中只能用一次next方法
      • 4.迭代器遍历时,不能用集合的方法进行增加或者删除;
      //创建集合对象
       Collection<String> c = new ArrayList<>();
      
       //添加元素
       c.add("hello");
       c.add("world");
      
       //Iterator<E> iterator():通过集合的iterator()方法获取集合中元素的迭代器
       Iterator<String> it = c.iterator();
      
       //循环元素的判断和获取
       while (it.hasNext()) {
           System.out.println(it.next());
       }
      
    • 增强for

      • 增强for 内部原理是一个Iterator迭代器,只有实现Iterable接口的类才可以使用迭代器和增强for
      for(集合/数组中元素的数据类型 变量名 :  集合/数组名) {// 已经将当前遍历到的元素封装到变量中了,直接使用变量即可}
      
    • lambda表达式

          /*lambda遍历(推算)*/
          
          //lambda: ()-> {}
          //coll.forEach((String s)->{System.out.println(s);});	    
          coll.forEach(s->System.out.println(s));
      

4) List集合(接口)

  • 概述

    • List 集合是一个存取有序的集合,用户可以精确控制列表中每个元素的插入位置, 用户可以通过整数索引访问元素, 并搜索列表中的元素。与Set集合不同, 列表通常允许重复的元素。
  • 特点

    • 存取有序
    • 允许重复
    • 有索引 (下标)
  • List集合的遍历方式

    • 迭代器

      Iterator<String> it = list.iterator();
      while (it.hasNext()) {
           String s = it.next();
           System.out.println(s);
           //coll.remove(s); //error: ConcurrentModificationException; 不能使用集合的删除方式在遍历中删除元素;(普通迭代器没有添加方法add)
           it.remove();     //right, 使用迭代器方法删除元素
       }
      
    • 列表迭代器

      //1. 列表迭代器创建时,里面的指针默认也是指向0索引的元素;
      //2. 列表迭代器比普通迭代器额外多了一个方法:在遍历的过程中,可以添加元素(迭代器遍历过程中,不能用集合的方法添加元素,可以用列表迭代器的方法添加元素)
      //3. List接口才有listIterator方法,Collector接口没有该方法
      //4. 列表迭代器还可以通过it2.hasPrevious()和 it2.previous()该两个方法进行反向遍历,但由于迭代器默认创建时在0索引位置,需要先next移动迭代器到末尾,才可以反向使用,局限性较大。
      ListIterator it2 = coll.listIterator();
      while (it2.hasNext()) {
          System.out.println(it2.next());
          it2.add("add test"); //right, 可以用列表迭代器的方法添加元素
      }
      it2.hasPrevious();
      it2.previous();
      
    • 增强for

       //快速生成增强for代码块快捷方式: 变量名 + '.' + for
       for (String s : list) {
           System.out.println(s);
       }
      
    • 匿名内部类

         //底层原理:forEach底层会进行一次遍历集合,依次得到每一个元素,然后得到的每一个元素传递给accept方法
         list.forEach(new Consumer<String>() {
             @Override
             public void accept(String s) {
                 System.out.println(s);
             }
         });
      
    • lambda

          /*lambda遍历(推算)*/
          
          //lambda: ()-> {}
          //list.forEach((String s)->{System.out.println(s);});	    
          list.forEach(s->System.out.println(s));
      
    • 普通for循环

      for (int i = 0; i < list.size(); i++) {
           //i:依次表示集合中的每一个索引
           String s = list.get(i);
           System.out.println(s);
       }
      
  • 总结:

    • 1.遍历过程中,当需要删除元素时,使用普通迭代器;
    • 2.遍历过程中,当需要增加元素时,使用列表迭代器;(普通迭代器不能增加元素)
    • 3.如果仅仅是遍历,使用增加for或者lambda;
    • 4.如果想遍历的过程中,操作索引,使用普通for;
    • 5.相较于List接口,Collection接口不支持列表迭代器遍历;同时也没有索引,因此不支持普通for循环遍历;

5) Set 集合(接口)

  • 不可以存储重复元素
  • 没有索引, 不能使用普通for循环遍历
  • Set 集合的遍历方式
    • 迭代器遍历
    • 增强for遍历
    • lambda遍历

5.1) TreeSet 集合(具体实现)

  • TreeSet 是Set接口的具体实现;
  • 不可以存储重复元素
  • 没有索引
  • 可以将元素按照规则进行排序
    • TreeSet():根据其元素的自然排序进行排序
    • TreeSet(Comparator comparator) :根据指定的比较器进行排序
  • 自然排序 Comparable 和 比较器排序 Comparator
    • 自然排序: 自定义类实现Comparable接口,重写compareTo方法,根据返回值进行排序
    • 比较器排序: 创建TreeSet对象的时候传递Comparator的实现类对象,重写compare方法,根据返回值进行排序
    • 在使用的时候,默认使用自然排序,当自然排序不满足现在的需求时,必须使用比较器排序
  • 两种方式中关于返回值的规则
    • 如果返回值为负数,表示当前存入的元素是较小值,存左边
    • 如果返回值为0,表示当前存入的元素跟集合中元素重复了,不存
    • 如果返回值为正数,表示当前存入的元素是较大值,存右边
  /*1.自然排序*/
  public class Student implements Comparable<Student>{
      ....
      @Override
      public int compareTo(Student o) {
          //按照对象的年龄进行排序
          //主要判断条件: 按照年龄从小到大排序
          int result = this.age - o.age;
          //次要判断条件: 年龄相同时,按照姓名的字母顺序排序
          result = result == 0 ? this.name.compareTo(o.getName()) : result;
          return result;
      }
  }
  //创建集合对象(自然排序)
  TreeSet<Student> ts = new TreeSet<>();

  /*2.比较器排序*/
  public class Teacher {
     ...
  }
  	//创建集合对象
    TreeSet<Teacher> ts = new TreeSet<>(new Comparator<Teacher>() {
        @Override
        public int compare(Teacher o1, Teacher o2) {
            //o1表示现在要存入的那个元素
            //o2表示已经存入到集合中的元素
          
            //主要条件
            int result = o1.getAge() - o2.getAge();
            //次要条件
            result = result == 0 ? o1.getName().compareTo(o2.getName()) : result;
            return result;
        }
    });

5.2) HashSet 集合(具体实现)

  • HashSet 是Set接口的具体实现;
  • 不可以存储重复元素;
  • 没有索引,不能使用普通for循环遍历;
  • 底层数据结构是哈希表;
    • 节点个数少于等于8个(数组 + 链表实现哈希表)
    • 节点个数多于8个(数组 + 红黑树实现哈希表)
    • 以上是JDK1.8以后的哈希表实现方式
  • ​ HashSet 集合存储自定义类型元素, 要想实现元素的唯一, 要求必须重写hashCode方法和equals方法。

6) Map集合(接口)

  • 概述
    • interface Map<K,V> K:键的类型;V:值的类型
  • 特点
    • 双列集合, 一个键对应一个值
    • 键不可以重复, 值可以重复。
  • Map集合的遍历
     //方法一:获取所有key的集合,通过遍历key的集合,获取到每一个key, 再根据key获取对应值
     for (String key: map.keySet()) {
         System.out.println(key + ": " + map.get(key));
     }
    
     //方法二:获取所有键值对对象的集合,遍历键值对对象的集合,得到每一个键值对对象, 再根据键值对对象获取键和值
     Set<Map.Entry<String, Integer>> entrySet = map.entrySet(); //键值对对象集合
     for (Map.Entry entry: entrySet) {
         System.out.println(entry.getKey() + ": " + entry.getValue());
     }
    

6.1) HashMap集合 (具体实现)

  • HashMap底层是哈希表结构的
  • 依赖hashCode方法和equals方法保证键的唯一
  • 如果键要存储的是自定义对象,需要重写hashCode和equals方法, 才能包装保证键的唯一, 否则默认会使用默认的hashCode和equals进行判断。

6.2) TreeMap集合(具体实现)

  • TreeMap底层是红黑树结构
  • 依赖自然排序或者比较器排序, 对键进行排序
  • 如果键存储的是自定义对象, 需要实现Comparable接口或者在创建TreeMap对象时候给出比较器排序规则。
  • 自然排序 Comparable 和 比较器排序 Comparator
    • 自然排序: 自定义类实现Comparable接口,重写compareTo方法,根据返回值进行排序

    • 比较器排序: 创建TreeMap对象的时候传递Comparator的实现类对象,重写compare方法,根据返回值进行排序。

      //自然排序
      public class Student implements Comparable<Student> {
      ...
      }
       Map<Student, String> studentMap = new TreeMap<>();
      
      //比较器排序
      Map<Student, String> stdMap = new TreeMap<>(new Comparator<Student>() {
          @Override
          public int compare(Student o1, Student o2) {
              return o1.getAge() - o2.getAge();
          }
      });
      

6.3) 区别

  • 相同点:
    • 均是Map接口的具体实现,集成了Map集合的基本特点;
    • 均是键不可以重复, 值可以重复;
  • 不同点:
    • 底层存储结构不。HashMap底层是哈希表结构,TreeMap底层是红黑树结构;
    • 判断重复的依据不同。在HashMap中判断key是否重复的依据是根据hash值和equals比较,但是在TreeMap中,判断key是否重复的依据是根据 comparaTo 是否为0,如果为0,TreeMap 就认为key是重复的
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 尚硅谷Java基础笔记是一套系统而全面的Java基础学习资料。这份笔记Java语言的基本概念介绍开始,涵盖了Java的核心知识点,包括变量、数据类型、运算符、流程控制等。接着,笔记详细讲解了面向对象编程的特点和Java中的类、对象、封装、继承、多态等内容。 在这份笔记中,还深入讨论了Java中的常用类库,如String、ArrayList、HashMap等,以及它们的常见用法和实例操作。此外,笔记还介绍了常见的异常处理机制,包括try-catch语句、throw和throws关键字等,帮助学习者理解并掌握Java中的错误和异常处理。 除了基础知识的讲解,尚硅谷Java基础笔记还提供了大量的例子和练习题,帮助学习者巩固所学内容,并通过实践提高编程能力。这些例子和练习题涵盖了各个知识点和应用场景,从简单到复杂,逐渐深入,非常有助于学习者的理解和应用能力的提升。 总而言之,尚硅谷Java基础笔记是一份详细、系统、易懂的学习资料,适合初学者入门学习Java编程。无论是对于零基础的学习者还是对于有一定编程经验的人来说,这份笔记都是一份不可多得的宝藏,可以帮助他们夯实Java基础,掌握编程技巧,为进一步扩展知识奠定坚实的基础。 ### 回答2: 尚硅谷java基础笔记是一份完整而详细的教程,对于初学者来说是一个很好的学习资源。 这份笔记由尚硅谷团队精心编写而成,包含了Java基础的各个方面。首先从Java的历史和应用领域入手,引导读者对Java有一个整体的认识。然后逐步介绍Java的发展环境和使用工具,让读者能够了解如何配置和使用Java开发环境。 接下来,笔记逐个介绍了Java的基本语法、数据类型、运算符、流程控制语句等基础知识。这些内容以简洁明了的方式呈现,附有实例和练习题,让读者可以通过练习巩固所学内容。 除了基础语法笔记还深入讲解了Java的面向对象编程思想和相关特性,包括类与对象、封装、继承、多态等。这部分内容对于理解Java的核心思想和编程范式非常重要,而且通过实例和案例的讲解,更加直观地展示了面向对象的优势和应用。 此外,笔记还包含了Java的常用类库的介绍,如集合框架、IO流、异常处理等,这些内容对于进行实际开发非常实用。 总之,尚硅谷java基础笔记内容全面、深入浅出,适合初学者学习和巩固基础知识。通过系统地学习这份笔记,读者可以建立起扎实的Java基础,为后续的学习和实践打下坚实的基础。同时,笔记中的案例和练习题也为读者提供了不少实践机会,帮助读者更好地理解和应用所学知识。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值