【JAVA数据结构】Java语法完善补充

简介: JAVA数据结构 & Java语法完善补充

JAVA数据结构 & Java语法完善补充

1. 包装类

包装类就是基本数据类型的“对象化版,用于适应泛型”

特殊的,int, char --> Integer, Character

其他基本数据类型仅仅将首字母变大写, boolean --> Boolean

1.1 装箱

显式装箱

int i = 10;
Integer i1 = Integer.valueOf();
Integer i2 = Integer.valueOf("10");
Integer i3 = new Integer(i);
Integer i4 = new Integer(10);


隐式装箱

int i = 10;
Integer i1 = i;
Integer i1 = (Integer)i;
System.sout.println(i);//这里相当于用了装箱成Integer, 用了Integer重写的toString()方法
//等等传参赋值一般都会隐式装箱
//但是!int[]  ---/---> Integer[]
//在集合的toArray()里面特别注意

1.2 拆箱

显式拆箱

Integer i = new Integer(10);
int i1 = i.intValue();


隐式拆箱

Integer i = new Integer(10);
int i1 = i;
int i1 = (int)i;
//但是!Integer[]  ---/---> int[]


1.3 Integer包装类的比较

如果用 <=, <, >=, >, ==, != 判断的话,只能判断-128 ~ 127,的整数

范围外就得用equals()和compareTo()

2. 泛型

类型参数化

适用于许多类型

泛型只能是引用类型,不能是基本数据类型

对于裸类型的使用,在讲编译器时会继续讨论哦【兼容老版本】

2.1 泛型语法

class 泛型类名称<类型列表T1, T2...> {

   ...

}


2.1.1 泛型在类中构造数组

在系统自带的很多泛型类中,很多都用到的技巧,就是先构造Object,在返回之前强转

Object[] elementData;
elementData = new new Object[initialCapacity];
elementData[0] = e;//(E e)Object 接收一切,在这完成了向上转型,等一下就不会出错
//用的时候向上转型
return (E)elementData[0];
//出的时候向下转型(因为是泛型向上转型过来的,所以没问题)


2.1.2 泛型方法

这个与泛型类的泛型无关

关键字之后,返回类型之前

public static <T> int fun(T t) {

   ...

}


最重要的是,传参必须是包含泛型的,才能人我知道是什么类型,有类型推导1


2.2 擦除机制

其实在查看字节码文件后,可以得知所有的泛型都被擦除成Object了

并且 ( ).class文件名中不含泛型

泛型只存在于编译阶段

能做到的就是,根据指定类型,传值自动向上转型以及返回值强制类型转化

2.3 通配符 —> ?

class B <T> {
    public void p() {
        System.out.println("打印成功");
    }
}
public class Test {
    public static void print(B<?> b) {
        b.p();
    }
    public static void main(String[] args) {
       B<String> b = new B<>();
       print(b);
    }
}
 public static <T> void print(B<T> b) {
        b.p();
}


通配符是可以用泛型方法模拟的

通配符额外的特点

但是通配符更简约,?问号表示未知类型待填充

通配符上界下界都有

上界:<? extends T> :> 代表填充的类型必须是T或者其子类【? <= T】

下界:<? super T> :> 代表填充的类型必须是T或者其父类【? >= T】

3. String常量池

在JVM中其实就是一个StringTable类,是一个固定大小的HashTable【哈希表】

接下来的内容如果听不懂,可以在学习完HashMap和HashSet后学习



hash默认为0,当求哈希值时才会改变(哈希表的哈希方法有关)

char[] 类型数组不用多说

public static void main(String[] args) {
    String str1 = "ahasdsadhh";
    String str2 = "ahasdsadhh";
    String str3 = new String("ahasdsadhh" );
    System.out.println(str1 == str2);
    System.out.println(str1 == str3);
}




接下来就是一些【规矩】

俩个或多个字面量直接相加,是在编译期间进行的,只会将结果存放在字符串常量池。

这个也很好理解,因为一条式子只用一个StringBuilder,最终StringBuilder,在把结果放进常量池

这也是体现了多次使用 【+= string】会徒增很多麻烦

String()构造方法

如果括号内已存在于常量池,则只需要拷贝一份

如果没有,则先在常量池中备份,再拷贝一份

不带参数,为空字符串。(当然也是从常量池拷贝的)

“” 空字符串也是字符串(应该放进常量池),就像空数组也是数组,只不过里面的一些成员属性空空如也

= “ … ”

如果“ … ”已存于常量池,则直接引用

如果没有,在常量池里面备份,直接引用

intern()方法【native方法,底层由C++写的】

理论上任何字符串应该都是存在于常量池里的,但是有一种情况没有

就是,String( char[] ),用char[]直接构造的字符串,拷贝一份直接给String,这样就不需要在常量池里拿了

public static void main(String[] args) {
    char[] ch = {'1', '2', '3', '4'};
    String str1 = new String(ch);
    String str2 = "1234";
    System.out.println(str1 == str2);
}



public static void main(String[] args) {
    char[] ch = {'1', '2', '3', '4'};
    String str1 = new String(ch);
    str1.intern();
    String str2 = "1234";
    System.out.println(str1 == str2);
}



通过手动导入常量池,刚才用字符数组构建的字符串放进了字符串常量池,该引用也顺理成章的指向了常量池。

4. 反射

照妖镜一样,反射到的类,所有东西都无处遁形


Java的反射(reflection)机制是指在程序的运行状态中,可以构造任意一个类的对象,可以了解任意一个对象所属的类,可以了解任意一个类的成员变量和方法,可以调用任意一个对象的属性和方法。


这种动态获取程序信息以及动态调用对象的功能称为Java语言的反射机制。反射被视为动态语言的关键。


在这个运行状态中,可以临时使用private属性以及方法(除了枚举里的,最强大常量的威严)


在学习框架之前,我们只需要了解反射即可


4.1 反射相关的类

重要的类 对应

Class类 类与接口

Field类 成员属性

Method类 成员方法

Constructor类 构造方法

【Class】 --> 【获取一个类】 —> 【制作成一个Class类】 ----> 【一面镜子 c1】

c1.getname(),可以获取到制作这面镜子的对应类的全路径

c1.newInstance() ,可以直接调用 不带参数的 符合权限 构造方法

Class.forname(不同的包的话具体路径),(制作镜子常见方法)

T.class(不在同个包中需要导包)

(new T()).getClass()or 具体引用.getClass()(提取引用变量的类制作成镜子)**(有“Declared”,则打包成数组)

以上三种制作出同一面镜子。

通过镜子c1反射到

Field 属性

一般用 c1.getDeclaredField(String name) 和 c1.getDeclaredFields() 属性(可以获取任何方法)

其中前者为单一成员属性,后者为全部打包出来的成员属性数组。

少了“Declared” 就只能用符合权限的成员属性

.set(具体引用, 传参...)

Method 方法

一般用 c1.getDeclaredMethod(String name, 匹配的类型.class ... ) 和 c1.getDeclaredMethods() 属性(可以获取任何方法)

其中前者为单一成员方法,后者为全部打包出来的成员方法数组。

少了“Declared” 就只能用符合权限的成员方法

.invoke(具体引用, 传参...)

Constructor 构造方法

一般用 c1.getDeclaredConstructor(匹配的类型.class ...) 和 c1.getDeclaredConstructors() 属性(可以获取任何方法)

其中前者为单一构造方法,后者为全部打包出来的构造方法数组。

少了“Declared” 就只能用符合权限的构造方法

.newInstance(传参...)

4.2 一个实例加深理解

总的来看,这是异常的处理,不然跑不过编译


可以用下面的 try & catch法(JVM处理不了就会被打印)

也可以直接抛给 JVM 处理

在操作的时候会抛很多编译时异常,所以要做好处理

   public static void main(String[] args){

       //一个类只有一个class对象

       try {

  区域一:

  区域二:

  区域三:

  区域四:

}catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }



4.2.1 区域一:【镜子制作】

Class<?> c1 = Class.forName("Student");
            Class<?> c2 = Student.class;
            Class<?> c3 = (new Student()).getClass();
            System.out.println(c1 == c2);
            System.out.println(c1 == c3);
            System.out.println(c3 == c2);
            Student student1 = (Student) c1.newInstance();//调用构造方法


4.2.2 区域二:【构造方法】

setAccessible(true)
            //getDeclared....s (加s,对应的所有东西都打包在数组里)
            Constructor<?> constructor = c1.getDeclaredConstructor(String.class, int.class);
            //确认一下是否使用private东西(私有仍可用)
            constructor.setAccessible(true);
            Student student2 = (Student) constructor.newInstance("小卡拉", 19);
            System.out.println(student2);
            constructor.setAccessible(false);

4.2.3 区域三:【成员属性】

Field field = c1.getDeclaredField("name");
            field.setAccessible(true);
            field.set(student1, "小霹雳");
            System.out.println(student1);
            constructor.setAccessible(false);


4.2.4 区域四:【成员方法】

Method method = c1.getDeclaredMethod("print", String.class);
            method.setAccessible(true);
            method.invoke(student1, "啊哈,我在调用这个私有方法");
            constructor.setAccessible(false);


4.2.5 测试:


4.3 优缺点

优点:可以运行时使用类里的所有方法,常用于一些框架

缺点:效率不高,要调用个private的东西

5. 枚举Enum抽象类

5.1 建立枚举类


对于枚举类而言,我们自己定义的枚举类默认继承系统的 Enum抽象类。


并且枚举类的private属性方法(其实默认的都是,绝对不能改成public)都不能被反射到


在Java中,枚举类型更加体现其“常量性”,而不是C语言枚举对应的是一个整型数字


体现在打印枚举类型,打印的是常量名(除非你重写了方法)

public enum TestEnum { //默认继承 Enum 这个抽象类
    //必须放在最上面,这是特殊的规定
    RED,
    GREEN(),//括号可省略
    BLACK;
    private String color;
    private int ordinal;
}

实际上,每个常量都是这个类的实例化常量!

索引就是这个类定义的前后顺序(0开始),索引没法变

并且用这个类实例化其他常量!

5.2 枚举类的常见使用场景

5.2.1 switch语句分支

public static void main(String[] args) {
        TestEnum testEnum = RED;
        //只能在枚举类内部使用,且不能自己实例化,只能用已有的赋值
        switch (testEnum) {
            case RED:
                System.out.println(RED);//打印常量名,除非你重写了toString();
                break;
            case BLACK:
                System.out.println(BLACK);
                break;
            case GREEN:
                System.out.println(GREEN);
                break;
            default:
                System.out.println("error");
                break;
        }
    }



5.2.2 构造方法

public enum TestEnum { //默认继承 Enum 这个抽象类
    //必须放在最上面,这是特殊的规定
    RED("red", 1),
    GREEN(),
    BLACK;
    private String color;
    private int ordinal;
    private TestEnum(String color, int ordinal) {
        this.color = color;
        this.ordinal = ordinal;
    }
    TestEnum() {
        //不带参数构造方法,自己写了构造方法后一定要补上
    }
}


5.2.4 常见的方法

public static void main1(String[] args) {
        TestEnum[] testEnum = TestEnum.values();
        System.out.println(RED.color);
        System.out.println(RED.ordinal);
        for(TestEnum t : testEnum) {
            System.out.print(t + " " + t.ordinal() + " "); //获取索引
        }
        TestEnum testEnum1 = TestEnum.valueOf("GREEN");//类似于 字符串转化具体值
        System.out.println(testEnum1);
        System.out.println(RED.compareTo(GREEN));//默认重写了,因为默认继承 Enum
    }

values():依照索引先后构建枚举常量数组

ordinal():获取枚举常量对应的索引

valueof():字符串转化(如果不是该枚举类型的任何一个常量,报异常)(大写的O)

枚举常量本身就是实例化后的,并且本身就重写了compareTo()和toString();


6. λ表达式(lambda)

6.1 lambda表达式的用途

重写方法(一般是抽象类,或者接口)

有一个要求,这个(抽象类or接口)只有一个可被重写的抽象方法

原因随后便知

6.2 lambda表达式的语法

(parameters) -> expression 或者 (parameters) -> { statements;}

parameters:参数列表

-> :【形参被用于】

express:表达式,作为返回值

statems:方法体,单语句或者多语句

显然,如果这个抽象类或者接口有多个抽象方法的话,是绝对用不了这个语法的(不支持重载)

使用这个表达式重点在于你很熟悉这个抽象类或者接口

() -> 2;
  x -> 2 * x;
  (x, y) -> x + y;
  (int x, int y) -> x + y;
  (String s) -> System.out.println(s);
//只有在单一参数,单一语句,单一表达式时是可以省略括号的,其他不行


6.3 函数式接口

就是一个接口只有一个抽象方法(@FunctionlInterface,这个注解可以用来警示我们写代码,规范行为)

下面这个是可以的,default修饰的方法不是抽象方法

这样这个接口的目的就相当于【传了一个方法过去】

@FunctionlInterface
interface A {
    void test();
    default void test2() {
        System.out.println("0.0");
    }
}


使用实例:

public class Test { 
    public static void main(String[] args) {
        A a = () -> {
            System.out.println("yes");
        };
        a.test();
    }
}


其他情况举一反三!


因为重写的方法只有一个,所以简化成这样


6.4 变量捕获

匿名内部类:可以捕获到该语句以上的”局部常量“(在整个局部范围中都没有变过的常量),捕获之前之后都不能改变它!

如果是成员变量等全局性质的变量,那一定能访问到~

引用类型不得更改指向~

lambda表达式:同上

int x = 1;
  x = 1//error
  A a = new A() {
        x = 2;//error
        System.out.println(x);//yes
    }
int x = 1;
A a = () -> {
    System.out.println(x);//yes
}

6.5 lambda 表达式在集合类以及Map类中的使用

这些接口很大概率是需要重写比较方法之类的…

6.5.1 典例1:

重写Collection / List 的extends的Iterable接口的forEach()所先需要的Consumer接口的accept()方法
public interface Iterable<T> {
    Iterator<T> iterator();
    default void forEach(Consumer<? super T> action) {
        Objects.requireNonNull(action);
        for (T t : this) {
            action.accept(t);
        }
    }
    default Spliterator<T> spliterator() {
        return Spliterators.spliteratorUnknownSize(iterator(), 0);
    }
}
@FunctionalInterface
public interface Consumer<T> {
    void accept(T t);
    default Consumer<T> andThen(Consumer<? super T> after) {
        Objects.requireNonNull(after);
        return (T t) -> { accept(t); after.accept(t); };
    }
}



熟悉接口很重要

List<String> list = new ArrayList<>();
list.add("1");
list.add("2");
list.add("3");
list.add("4");
List.forEach(s -> {
  System.out.print(s + " ");  
});
//如果不重写,这个方法压根用不了
//结果:1 2 3 4

6.5.2 典例2:

重写Collections工具类的sort()方法,List的sort(),所需要的Comparator接口的compare( , )方法

default void sort(Comparator<? super E> c) {
        Object[] a = this.toArray();
        Arrays.sort(a, (Comparator) c);
        ListIterator<E> i = this.listIterator();
        for (Object e : a) {
            i.next();
            i.set((E) e);
        }
}
@FunctionalInterface
public interface Comparator<T> {
    int compare(T o1, T o2);
    boolean equals(Object obj);
}
//这里很特殊,有两个抽象方法,但是并无大碍,可以这么理解为equals()在Object中本就有了
//或者不要在意这一点,记住就好
List<String> list = new ArrayList<>();
list.add("1");
list.add("2");
list.add("3");
list.add("4");
List.sort((s1, s2)-> s2.compareTo(s1));//逆序
System.out.println(list);
//结果:[4, 3, 2, 1]


6.5.3 典例3:

HashMap对应的forEach();

TreeMap的话还需要比较方法才能构建树

这次是重写BiConsumer接口的accept()

它的forEach(),是两个参数

Map<Integer, String> hashMap = new HashMap<>();
hashMap.put(1, "11");
hashMap.put(2, "22");
hashMap.put(3, "33");
hashMap.put(4, "44");
hashMap.put(5, "55");
hashMap.forEach((k, v) -> {
    System.out.println(k + "=" + v);
})
//结果:
//1 = 11...


6.6 优缺点

显然,代码层次上,lambda表达式很简洁

但是代码不易读不易调试

文章到此结束!谢谢观看

可以叫我 小马,我可能写的不好或者有错误,但是一起加油鸭🦆!


这是我的代码仓库!(在马拉圈的23.2里)代码仓库 代码具体位置


就是在一些情况写编译器会识别你的泛型是什么,从而省略<>中的泛型甚至不写<>。


s:103
+关注
目录
打赏
0
0
0
0
34
分享
相关文章
Kotlin语法笔记(26) -Kotlin 与 Java 共存(1)
Kotlin语法笔记(26) -Kotlin 与 Java 共存(1)
56 2
Kotlin语法笔记(26) -Kotlin 与 Java 共存(1)
本系列教程笔记详细讲解了Kotlin语法,适合需要深入了解Kotlin的开发者。若需快速学习Kotlin,建议查看“简洁”系列教程。本期重点介绍了Kotlin与Java的共存方式,包括属性、单例对象、默认参数方法、包方法、扩展方法以及内部类和成员的互操作性。通过这些内容,帮助你在项目中更好地结合使用这两种语言。
73 1
|
5月前
|
Java中的HashMap和TreeMap,通过具体示例展示了它们在处理复杂数据结构问题时的应用。
【10月更文挑战第19天】本文详细介绍了Java中的HashMap和TreeMap,通过具体示例展示了它们在处理复杂数据结构问题时的应用。HashMap以其高效的插入、查找和删除操作著称,而TreeMap则擅长于保持元素的自然排序或自定义排序,两者各具优势,适用于不同的开发场景。
67 1
Java 集合江湖:底层数据结构的大揭秘!
小米是一位热爱技术分享的程序员,本文详细解析了Java面试中常见的List、Set、Map的区别。不仅介绍了它们的基本特性和实现类,还深入探讨了各自的使用场景和面试技巧,帮助读者更好地理解和应对相关问题。
67 5
|
3月前
|
java do while 的语法怎么用?
java do while 的语法怎么用?
84 3
本文聚焦于Java内存管理与调优,介绍Java内存模型、内存泄漏检测与预防、高效字符串拼接、数据结构优化及垃圾回收机制
在现代软件开发中,性能优化至关重要。本文聚焦于Java内存管理与调优,介绍Java内存模型、内存泄漏检测与预防、高效字符串拼接、数据结构优化及垃圾回收机制。通过调整垃圾回收器参数、优化堆大小与布局、使用对象池和缓存技术,开发者可显著提升应用性能和稳定性。
79 6
java Runtime.exec()执行shell/cmd命令:常见的几种陷阱与一种完善实现
java Runtime.exec()执行shell/cmd命令:常见的几种陷阱与一种完善实现
101 1
Java中的数据结构:ArrayList和LinkedList的比较
【10月更文挑战第28天】在Java编程世界中,数据结构是构建复杂程序的基石。本文将深入探讨两种常用的数据结构:ArrayList和LinkedList,通过直观的比喻和实例分析,揭示它们各自的优势与局限,帮助你在面对不同的编程挑战时做出明智的选择。
Java 中常用的数据结构
【10月更文挑战第20天】这些数据结构在 Java 编程中都有着广泛的应用,掌握它们的特点和用法对于提高编程能力和解决实际问题非常重要。
65 6
Kotlin语法笔记(28) -Kotlin 与 Java 混编
Kotlin语法笔记(28) -Kotlin 与 Java 混编
79 2
AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等