9.18的笔记,包含泛型内容,String内容

 

链接:https://github.com/Snailclimb/JavaGuide

1、char和string的区别

字符型常量和字符串常量的区别?

  1. 形式上: 字符常量是单引号引起的一个字符; 字符串常量是双引号引起的0个或若干个字符
    1. 之前一直没注意char类型是不能为''的
  2. 含义上: 字符常量相当于一个整型值( ASCII 值),可以参加表达式运算; 字符串常量代表一个地址值(该字符串在内存中存放位置)
  3. 占内存大小 字符常量只占 2 个字节; 字符串常量占若干个字节 (注意: char 在 Java 中占两个字节),

自增自减运算符,面试遇到过这种题,还愣了一下

泛型

链接:https://github.com/Snailclimb/JavaGuide

链接:https://juejin.im/post/6844903917835419661

重中之重

Java泛型了解么?什么是类型擦除?介绍一下常用的通配符?

Java 泛型(generics)是 JDK 5 中引入的一个新特性, 泛型提供了编译时类型安全检测机制,该机制允许程序员在编译时检测到非法的类型。泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。

 

泛型好处

在没有泛型的情况的下,通过对类型 Object 的引用来实现参数的“任意化”,“任意化”带来的缺点是要做显式的强制类型转换,而这种转换是要求开发者对实际参数类型可以预知的情况下进行的。对于强制类型转换错误的情况,编译器可能不提示错误,在运行的时候才出现异常,这是本身就是一个安全隐患。

Java的泛型是伪泛型,这是因为Java在编译期间,所有的泛型信息都会被擦掉,这也就是通常所说类型擦除 。 更多关于类型擦除的问题,可以查看这篇文章https://www.cnblogs.com/wuqinglong/p/9456193.html

泛型的一个通俗易懂的概念:看的黑马程序员视频

泛型类

在不指定泛型类型时,使用Object类型,默认使用所有类型的父类,什么都能存。

package string.test;

class MyGerneric <E>{
    E name;

    public MyGerneric(E name) {
        this.name = name;
    }

    public E getName() {
        return name;
    }

    public void setName(E name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "MyGerneric{" +
                "name=" + name +
                '}';
    }
}
//1.泛型类:
public class GernericTest{
    public static void main(String[] args) {
        MyGerneric myGerneric = new MyGerneric(new Object());
        System.out.println(myGerneric.toString());
    }



}

泛型方法:

1、普通类+泛型方法

class MyGerneric {
    public <T> void printMyGerneric(T t){
        System.out.println(t);
    }
}
//2.泛型方法:
public class GernericTest{
    public static void main(String[] args) {
        MyGerneric myGerneric = new MyGerneric();
        myGerneric.printMyGerneric(1);
        myGerneric.printMyGerneric("a");
        myGerneric.printMyGerneric('c');
        myGerneric.printMyGerneric(new Object());
    }
}

2、泛型类+泛型方法(泛型方法中声明了该方法使用的泛型,那么类声明的那个泛型T和方法声明的泛型T可以不同)

应用时,泛型类声明的T可以和泛型方法声明的T不一样

class MyGerneric <T>{
    public <T> void printMyGerneric(T t){
        System.out.println(t);
    }
}
//2.泛型方法:
public class GernericTest{
    public static void main(String[] args) {
        MyGerneric<String> myGerneric = new MyGerneric<>();
        myGerneric.printMyGerneric(1);
        myGerneric.printMyGerneric("a");
        myGerneric.printMyGerneric('c');
        myGerneric.printMyGerneric(new Object());
    }
}

3、泛型类+泛型方法(泛型方法中没有声明使用的泛型)

4、普通类+静态泛型方法

class MyTest2{
   static <T> void print(T t){
       System.out.println(t);
   }

}
public class Test2 {
    public static void main(String[] args) {
        MyTest2.print("a");
        MyTest2.print(1);
    }
}

 

顺便在这里写一下前几天的一个静态变量的选择题,突然想起来的

class MyTest2{
    static int a=10;
}
public class Test2 {
    public static void main(String[] args) {
        MyTest2 myTest2 = new MyTest2();
        myTest2.a++;
        MyTest2.a++;
        System.out.println(MyTest2.a);//打印结果是12
    }
}

泛型接口:

  含有泛型的接口,使用方式:2种
     1、定义接口的实现类,实现类中指定接口的泛型
     2、定义接口的实现类,实现类中不指定接口的泛型,创建实现类的对象时,指定类型
 

package string.test;

interface MyGerneric <T>{
    //含有泛型的接口,使用方式:
    // 1、定义接口的实现类,指定接口的泛型
    // 2、定义接口的实现类,不指定接口的泛型,创建实现类的对象时,指定类型

    //顺便再复习一下,泛型方法中声明<T>与不声明的区别
    <T> void printMyGerneric(T t);
    void printMyGerneric2(T t);
}

//第一种实现类,类中就定义了数据类型
class GernericTestImpl implements MyGerneric<String>{
    @Override
    public <T> void printMyGerneric(T t) {
        System.out.println(t);
    }


    @Override
    public void printMyGerneric2(String s) {
        System.out.println(s);
    }
}
class  GernericTestImpl2<T> implements MyGerneric<T>{
    @Override
    public <T> void printMyGerneric(T t) {
        System.out.println(t);
    }

    @Override
    public void printMyGerneric2(T t) {
        System.out.println(t);
    }
}
public class MyGernericTest {
    public static void main(String[] args) {
        GernericTestImpl gernericTestImpl = new GernericTestImpl();
        GernericTestImpl2<String> gernericTestImpl2 = new GernericTestImpl2<>();
        gernericTestImpl.printMyGerneric(new Object());
        gernericTestImpl.printMyGerneric("a");
        gernericTestImpl2.printMyGerneric(new Object());
        gernericTestImpl2.printMyGerneric("a");
        gernericTestImpl2.printMyGerneric2("a");
        //gernericTestImpl2.printMyGerneric2(new Object());//编译不通过,只能是String类型的参数
    }
}

通配符:

我们在定义泛型类,泛型方法,泛型接口的时候经常会碰见很多不同的通配符,比如 T,E,K,V 等等,这些通配符又都是什么意思呢?

本质上这些个都是通配符,没啥区别,只不过是编码时的一种约定俗成的东西。比如上述代码中的 T ,我们可以换成 A-Z 之间的任何一个 字母都可以,并不会影响程序的正常运行,但是如果换成其他的字母代替 T ,在可读性上可能会弱一些。通常情况下,T,E,K,V,? 是这样约定的:

  • ? 表示不确定的 java 类型
  • T (type) 表示具体的一个java类型
  • K V (key value) 分别代表java键值中的Key Value
  • E (element) 代表Element

?通配符

<?>:这个通配符不需要声明,可以直接使用,可以代表任意的数据类型。 当返回的类型未知时,可以使用这个通配符。只能接受数据,不能往集合中存储数据。

这几种试图存数据的都是非法的

这种合法得写法:不写<>,但是他的意思是使用默认的类型Object,也相当于指定了类型的。

?的使用

public class Main{

    public static void main(String[] args) {
        ArrayList<Integer> list1 = new ArrayList<>();
        ArrayList<String> list2 = new ArrayList<>();
        list1.add(1);
        list1.add(2);
        list2.add("a");
        list2.add("b");
        printList(list1);
        printList(list2);
    }

    private static void printList(ArrayList<?> list) {
        Iterator<?> iterator = list.iterator();

        while(iterator.hasNext()){
            Object obj = iterator.next();
            System.out.println(obj);
        }
    }
}

泛型是没有继承的概念的,不能试图使用Object类接受对象,但是可以使用上限下限的限定。

 

泛型的受限:上限限定和下限限定(面试中好像不常问)

使用:

泛型上限,用? extends E,使用的泛型必须是E的子类或本身
泛型下限,用? super E,使用的泛型必须是E的父类或本身
package string.test;
//此处T可以随便写为任意标识,常见的如T、E、K、V等形式的参数常用于表示泛型
//在实例化泛型类时,必须指定T的具体类型

import java.util.ArrayList;
import java.util.Iterator;

//1.泛型类:
public class Main{

    public static void main(String[] args) {
        //泛型上限,用? extends E,使用的泛型必须是E的子类或本身
        //泛型下限,用? super E,使用的泛型必须是E的父类或本身
        ArrayList<Integer> list1 = new ArrayList<>();
        ArrayList<String> list2 = new ArrayList<>();
        ArrayList<Number> list3 = new ArrayList<>();
        ArrayList<Object> list4 = new ArrayList<>();
        printList1(list1);//正确  list1是Integer类型,printList1泛型的上限为Number类型
//        printList1(list2);//编译不通过,list2是String类型,printList1泛型的上限为Number类型
        printList1(list3);//正确  list3是Number类型,printList1泛型的上限为Number类型
//        printList1(list4);//编译不通过  list4是Object类型,printList1泛型的上限为Number类型
//        printList2(list1);//编译不通过, list1是Integer类型,printList泛型的下限为String
        printList2(list2);//正确, list2是String类型,printList泛型的下限为String
//        printList2(list3);//编译不通过, list3是Number类型,printList泛型的下限为String
        printList2(list4);//正确, list4是Object类型,printList泛型的下限为String
    }

    private static void printList1(ArrayList<? extends Number> list) {
        Iterator<?> iterator = list.iterator();

        while(iterator.hasNext()){
            Object obj = iterator.next();
            System.out.println(obj);
        }
    }
    private static void printList2(ArrayList<? super String> list) {
        Iterator<?> iterator = list.iterator();

        while(iterator.hasNext()){
            Object obj = iterator.next();
            System.out.println(obj);
        }
    }
}

 

? 和 T 的区别

答:

区别1:通过 T 来 确保 泛型参数的一致性。

区别2:T可以多重限定而?不行。

区别3:?可以使用超类限定而T不行。

详细说明

1、?是不确定的类型,T是确定的类型。

T 是一个 确定的 类型,通常用于泛型类和泛型方法的定义,?是一个 不确定 的类型,通常用于泛型方法的调用代码和形参,不能用于定义类和泛型方法。

个人理解:(可能有错)

下面这个例子中,使用泛型T时候,可以保证list1集合中的数据类型是一致的都是T,而?不能确定唯一的数据类型,可以联想一下?extends T这种用法,?可以是多种类型。

2、

使用 & 符号设定多重边界(Multi Bounds),指定泛型类型 T 必须是 MultiLimitInterfaceA 和 MultiLimitInterfaceB 的共有子类型,此时变量 t 就具有了所有限定的方法和属性。对于?通配符来说,因为它不是一个确定的类型,所以不能进行多重限定。

3、

类型参数 T 只具有 一种 类型限定方式:

T extends A
复制代码

但是通配符 ? 可以进行 两种限定:

? extends A
? super A

class<T>和class<?>的区别

最常见的是在反射场景下的使用,这里以用一段发射的代码来说明下。

// 通过反射的方式生成  multiLimit 
// 对象,这里比较明显的是,我们需要使用强制类型转换
MultiLimit multiLimit = (MultiLimit)Class.forName("com.glmapper.bridge.boot.generic.MultiLimit").newInstance();

对于上述代码,在运行期,如果反射的类型不是 MultiLimit 类,那么一定会报 java.lang.ClassCastException 错误。

对于这种情况,则可以使用下面的代码来代替,使得在在编译期就能直接 检查到类型的问题:

Class<T> 在实例化的时候,T 要替换成具体类。Class<?> 它是个通配泛型,? 可以代表任何类型,所以主要用于声明时的限制情况。比如,我们可以这样做声明:

// 可以
public Class<?> clazz;
// 不可以,因为 T 需要指定类型
public Class<T> clazzT;

所以当不知道定声明什么类型的 Class 的时候可以定义一 个Class<?>。

那如果也想 public Class<T> clazzT; 这样的话,就必须让当前的类也指定 T ,

public class Test3<T> {
    public Class<?> clazz;
    // 不会报错
    public Class<T> clazzT;

==与equals


==:基本数据类型==比较的是值,引用数据类型==比较的是内存地址

equals():

    它的作用也是判断两个对象是否相等,它不能用于比较基本数据类型的变量equals()方法存在于Object类中,而Object类是所有类的直接或间接父类。Object的equals方法:

public boolean equals(Object obj) {
     return (this == obj);
}

equals使用时,要看一下该类有没有重写这个方法。

 hashCode()与 equals()

面试官可能会问你:“你重写过 hashcode 和 equals 么,为什么重写 equals 时必须重写 hashCode 方法?”

1)hashCode()介绍:

hashCode() 的作用是获取哈希码,也称为散列码;它实际上是返回一个 int 整数。这个哈希码的作用是确定该对象在哈希表中的索引位置。hashCode() 定义在 JDK 的 Object 类中,这就意味着 Java 中的任何类都包含有 hashCode() 函数。另外需要注意的是: Object 的 hashcode 方法是本地方法,也就是用 c 语言或 c++ 实现的,该方法通常用来将对象的 内存地址 转换为整数之后返回。

public native int hashCode();

散列表存储的是键值对(key-value),它的特点是:能根据“键”快速的检索出对应的“值”。这其中就利用到了散列码!(可以快速找到所需要的对象)

我们以“HashSet” 如何检查重复”为例子来说明为什么要有 hashCode
计算hashCode比equals执行快,然后先计算出hashCode后,与集合中的每个元素来比较,没有重复的,就说明元素不在集合中,有重复的,再equals是否相等,来判断,减少equals的次数,提升效率。

笔试完看:https://www.cnblogs.com/skywang12345/p/3324958.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值