栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

扩展内容(实时更新)

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

扩展内容(实时更新)

导航:
1.map
1.1 HashMap的底层实现原理?(以jdk7为例)
1.2 HashMap的底层实现原理?(以jdk8为例)
1.3 Map实现类:Properties
2.内部类
3.Java 8中关于接口的改进

Map 双列数据,存储key-value对的数据 接口就是规范

1/-Map:
双列数据,存储key-value对的数据 —类似于高中的函数: y = f(x)
2/-HashMap:
作为Map的主要实现类;线程不安全的,效率高;存储null的key和value

HashMap的底层:数组+链表(jdk7及之前)
数组+链表+红黑树(jdk 8)

3/-linkedHashMap:
保证在遍历map元素时,可以按照添加的顺序实现遍历。
原因:
在原有的HashMap 底层结构基础上,
添加了一对指针,指向前一个和后一个元素。

对于频繁的遍历操作,此类执行效率高于HashMap。

2/-TreeMap:
保证按照添加的key-value对进行排序,实现排序遍历。
此时考虑key的自然排序或定制排序

底层使用红黑树

2/-Hashtable:
作为古老的实现类;线程安全的,效率低;不能存储null 的key和value
3 /-Properties:
常用来处理配置文件。key和vaLue 都是String类型

快捷键:
ctrl shift t 查询类
ctrl o 查看类里面的所有方法
alt + 左右 向前,向后

面试题:

  1. HashMap的底层实现原理?
  2. HashMap和Hashtable的异同?
  3. CurrentHashMap与Hashtable的异同? ( 暂时不讲)

Map结构的理解:
Map中的key: 无序的、不可重复的,使用Set存储所有的key
—> key所在的类要重写equals()和hashCode() ( 以HashMap为例)

Map中的value: 无序的、可重复的,使用collection存储所有 的value
—>value所在的类要 重写equals()

一个键值对: key-value构成了一个Entry对象。
Map中的entry: 无序的、不可重复的,使用Set 存储所有的entry

HashMap的底层实现原理?(以jdk7为例)

HashMap map = new HashMap():

在实例化以后,底层创建了长度是16的一维数组Entry[] table.
…可能已经执行过多次put…

map. put (key1, value1):

首先, 调用key1 所在类的hashCode()计算key1哈希值,
此哈希值经过某种算法计算以后,得到在Entry 数组中的存放位置。

如果此位置上的数据为空 ,此时的key1-value1添加成功。----情况1

如果此位置上的数据不为空 ,
( 意味着此位置上存在一个或多个数据(以链表形式存在)),
比较key1和已经存在的一一个或多个数据的哈希值:

==》 如果 key1的哈希值与已经存在的数据的哈希值都不相同,
此时key1-value1 添加成功。---- 情况2

如果 key1的哈希值和已经存在的某一个数据(key2-value2)的哈希值相同,
继续比较: 调用key1所在类的equals (key2)方法,比较:
== 》如果 equals()返回false :此时key1-value1添加成功。---- 情况3
如果 equals().返回true:使用value1替换vaLue2。

补充:
关于情况2和情况3: 此时key1 -value1和原来的数据以链表的方式存储。

扩容:
在不断的添加过程中,会涉及到扩容问题,
当超出临界值(且要存放的位置非空)时,
默认的扩容方式: 扩容为原来容量的2倍,并将原有的数据复制过来。
(一般到了大于12,提前扩容)

HashMap的底层实现原理?(以jdk8为例)

jdk8相较于jdk7在底层实现方面的不同:

  1. new HashMap():底层没有创建一个长度为16的数组
  2. jdk 8底层的数组是: Node[], 而非Entry[]
  3. 首次调用put()方法时,底层创建长度为16的数组
  4. jdk7底层结构只有:数组+链表。jdk8中底层结构:数组+链表+红黑树。
    当数组的某一个索引位置上的元素以链表形式存在的数据个数> 8
    且当前数组的长度> 64时,
    此时此索引位置上的所有数据改为使用红黑树存储。

HashMap源码中的重要常量:

  • DEFAULT_ INITIAL CAPACITY : HashMap的默认容量, 16
  • DEFANLT_ LOAD_FACTOR:HashMap的默认加载因子:0.75
  • threshold:扩容的临界值,=容量*填充因子:16*0.75=>12
  • TREEIFY_THRESHOLD:
    Bucket中链表长度大于该默认值,转化为红黑树:8
  • MIN_REEIFY_CAPACITY:桶中的Node被树化时最小的hash表容量:64

Map实现类:Properties

  • Properties类是Hashtable的子类,该对象用于处理属性文件

  • 由于属性文件里的key、 value 都是字符串类型,
    所以Properties里的key和value都是字符串类型

  • 存取数据时,建议使用setProperty(String key, String value)方法
    和getProperty(String key)方法

Properties properties = new Properties();//实例化Properties对象
properties.load(new FileInputStream("jdbc.properties");//加载(读取)文件(以流的形式)
String user = properties.getProperty("user");//获取文件中的数据
利用反射,获取类的对象
Class c = Class.forName(user); //类类型的对象
Object o = c.newInstance();//类的对象
System.out.println(o);

==》 灵活性高 只需要改配置文件里的 不需要jdk环境

如果出现乱码:

打勾勾上,就不会出现乱码了,并且文件要重新造

内部类
当一个事物的内部,还有一个部分需要一个完整的结构进行描述,
而这个内部的完整的结构又只为外部事物提供服务,
那么整个内部的完整结构最好使用内部类。

●在Java中,允许一个类的定义位于另一个类的内部,
前者称为内部类,后者称为外部类。

●Inner class 一般用在定义它的类或语句块之内,
在外部引用它时必须给出完整的名称。

Inner class的名字不能与包含它的外部类类名相同;

分类:
成员内部类(static成员内部类和非static成员内部类)
局部内部类(不谈修饰符)、匿名内部类

  1. Java中允许将一个类A声明在另一个类B中,
    则类A就是内部类,类B称为外部类
  2. 内部类的分类:成员内部类(静态、非静态)VS局部内部类(方法内、代码块内、构造器内)
  3. 成员内部类:
    一方面,作为外部类的成员:
    1.调用外部类的结构
    2.可以被static修饰
    3.可以被4种不同的权限修饰
    另一方面,作为一个类:
    1.类内可以定义属性、方法、构造器等
    2.可以被final修饰, 表示此类不能被继承。
    言外之意,不使用final,就可以被继承
    3.可以被abstract修饰
  4. 关注如下的3个问题
    4.1如何实例化成员内部类的对象
    4.2如何在成员内部类中区分调用外部类的结构
    4.3开发中局部内部类的使用 见 InnerClassTest1
public class InnerClassTest {
    public static void main(String[] args) {
        //创建Dog实例(静态成员内部类)
        Person.Dog dog = new Person.Dog();
        dog.show();// ==>     卡拉是条狗
//      创建Bird实例(非静态成员内部类)
//      Person.Bird bird = new Person.Bird();错误的 外部类实例了才能调
        Person p = new Person();
        Person.Bird bird = p.new Bird();//有了对象之后调内部结构
        bird.sing();// ==>     我是一只小小鸟 人吃饭  0
        bird.display("z");// ==>     z   y   x
    }
}
class Person{//外部类不能被static修饰 并且只能是默认和公共
    String name = "x";
    int age;
    public void eat(){
        System.out.println("人吃饭");
    }
    //静态成员内部类
    static class Dog{
        String name;
        int age;
        public void show(){
            System.out.println("卡拉是条狗");
        }
    }
    //非静态成员内部类
    class Bird{
        String name = "y";
        public Bird(){}
        public void sing(){
            System.out.println("我是一只小小鸟");
            eat();
    //没有重名 可以省略前面的 是等价于Person.this.eat(); 调用外部类的非静态属性
            System.out.println(age);
        }
        public void display(String name){
            System.out.println(name);//方法的形参   z
            System.out.println(this.name);
            // display 所在方法类的name 内部类的属性  y
            System.out.println(Person.this.name);//外部类的属性  z
        }
    }
    public void method(){
        class AA{}//局部内部类
    }
    {
        class BB{}//局部内部类
    }
    public Person(){
        class CC{}//局部内部类
    }
}

InnerClassTest1

public class InnerClassTest1 {
    //开发中很少见
    public void method(){
        class AA{}//局部内部类
    }
    //里边用,外部不用
    //返回了一个实现了Compareable接口的类的对象
    public Comparable getComparable(){
        //创建一个实现了Compareable接口的类:局部内部类
        //方式一:
//        class MyComparable implements Comparable {
//            @Override
//            public int compareTo(Object o) {
//                return 0;
//            }
//        }
//        return new MyComparable();//返回个对象
        //方式二:创建了实现上面接口的匿名实现类的匿名对象
        return new Comparable() {
            @Override
            public int compareTo(Object o) {
                return 0;
            }
        };
    }
}

Java 8中关于接口的改进
Java 8中,你可以为接口添加静态方法和默认方法。
从技术角度来说,这是完全合法的,
只是它看起来违反了接口作为一个抽象定义的理念。

静态方法: 使用static关键字修饰。可以通过接口直接调用静态方法,
并执行其方法体。我们经常在相互一起使用的类中使用静态方法。
你可以在标准库中找到像Collection/Collections
或者Path/Paths这样成对的接口和类。

默认方法: 默认方法使用default关键字修饰。
可以通过实现类对象来调用。
我们在已有的接口中提供新方法的同时,还保持了与旧版本代码的兼容性。
比如:
java 8 API中对CollectionListComparator等接口提供了丰富的默认方法。

如何定义接口: 定义接口中的成员

JDK7及以前: 只能定义全局常量和抽象方法
全局常量: public static final的。 但是书写时,可以省略不写
抽象方法: public abstract的。

JDK8:
除了定义全局常量和抽象方法之外,还可以定义静态方法、默认方法(略)

CompareA
public interface CompareA {
    //静态方法
    public static void method1(){
        System.out.println("CompareA:北京");
    }
    //默认方法
    public default void method2(){
        System.out.println("CompareA:上海 ");
    }//Public 可以省略
    default void method3(){
        System.out.println("CompareA:上海");
    }
}

CompareB
public interface CompareB {
    default void method3(){
        System.out.println("CompareB:上海");
    }
}

SuperClass
public class SuperClass {
    public void method3(){
        System.out.println("SuperClass:北京");
    }
}

SubClassTest
public class SubClassTest {
    public static void main(String[] args) {
        SubClass s = new SubClass();
//        s.method1();
//        1.接口中定义的静态方法,只能通过接口来调用  ==>利用这个可以变成工具类
        CompareA.method1();//  ==》CompareA:北京
//          2.通过实现类的对象, 可以调用接口中的默认方法
//          如果实现类重写了接口中的默认方法,调用时,依然调用的是重写的方法
        s.method2();//  ==》SubClass:上海
//          3.如果子类(或实现类)继承的父类和实现的接口中声明了同名同参数的方法,
//            那么子类在没有重写此方法的情况下,默认调用的是父类中的同名同参数的方法。--》类优先原则  仅仅 针对当前有效
        s.method3();//  ==》SuperClass:北京 ==> SubClass:深圳
//            4.如果实现类实现了多个接口,而这多个接口中定义了同名同参数的默认方法
//            那么在实现类没有重写此方法的情况下,报错. ==>接口冲突
//              如果你把extends SuperClass去掉  就会报错 因为不知道调哪个method3()
//        结果就是: 必须在实现类中重写此方法
    }
}
class SubClass extends SuperClass implements CompareA,CompareB {
    public void method2(){
        System.out.println("SubClass:上海 ");
    }
    public void method3(){
        System.out.println("SubClass:深圳");
    }
//    5.如何在子类(实现类)的方法中调用父类接口中被重写的方法
    public void myMethod(){
        method3();//调用自己定义的重写的方法
        super.method3();//调用的是父类中声明的
        //调用接口中的默认方法
        CompareA.super.method3();
        CompareB.super.method3();
    }
}

----2021.11.08

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/446008.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号