文章目录
泛型
JDK1.5加入的新特性,泛型的本质是参数化类型,即操作的数据类型被指定为一个参数。安全简单,且所有的强制类型转换都是隐式和自动进行的,提高代码的重用率。
定义
概念:
将对象类型作为参数,指定到其它类或者方法上,从而保证类型转换的安全性和稳定性。
语法:
类型<E> 对象 = new 类型<E>();
- 解析:
<>
是泛型的特征E
表示某种数据类型,也可用其它字母,实际使用中需要用明确类型换掉 E
深入了解
泛型接口,类,方法
-
泛型接口:
-
泛型接口与泛型类的定义及使用基本相同。泛型接口常被用在各种类的生产器中。
-
只能用在抽象方法上
-
public interface Generator<T> { public T next(); } /** * 未传入泛型实参时,与泛型类的定义相同,在声明类的时候,需将泛型的声明也一起加到类中 * 即:class FruitGenerator<T> implements Generator<T>{ * 如果不声明泛型,如:class FruitGenerator implements Generator<T>,编译器会报错:"Unknown class" */ class FruitGenerator<T> implements Generator<T>{ @Override public T next() { return null; } }
-
-
泛型类:
-
用于类的定义中,被称为泛型类
-
最典型的就是各种容器类,如:List、Set、Map
-
只能用在成员变量上,只能使用引用类型
-
class 类名称 <E[,T]>{ ..... }
-
泛型类,是在实例化类的时候指明泛型的具体类型;泛型方法,是在调用方法的时候指明泛型的具体类型 。
-
-
泛型方法
-
是在调用方法的时候指明泛型的具体类型 。
-
返回值前面加上
-
/** * 泛型方法的基本介绍 * @param tClass 传入的泛型实参 * @return T 返回值为T类型 * 说明: * 1)public 与 返回值中间<T>非常重要,可以理解为声明此方法为泛型方法。 * 2)只有声明了<T>的方法才是泛型方法,泛型类中的使用了泛型的成员方法并不是泛型方法。 * 3)<T>表明该方法将使用泛型类型T,此时才可以在方法中使用泛型类型T。 * 4)与泛型类的定义一样,此处T可以随便写为任意标识,常见的如T、E、K、V等形式的参数常用于表示泛型。 */ public <T> T genericMethod(Class<T> tClass)throws InstantiationException , IllegalAccessException{ T instance = tClass.newInstance(); return instance; }
-
泛型方法和可变参数
-
public <T> void printMsg( T... args){ for(T t : args){ Log.d("泛型测试","t is " + t); } }
泛型的继承
-
package com.bigdata.practice1011; public abstract class FanXin <T1, T2>{ T1 name; public abstract void setName(T2 sex); }
-
保留父类泛型
-
// 全保留 package com.bigdata.practice1011; // 泛型父类 子类全部保留父类的泛型 public class FanXin1<T1,T2> extends FanXin<T1,T2>{ @Override public void setName(T2 sex) { } } // 保留一个 package com.bigdata.practice1011; // 继续全写泛型不合适 得把用的那个泛型写成具体类型 public class FanXin2<T1> extends FanXin<T1, String> { @Override public void setName(String sex) { } }
-
-
不保留父类泛型
-
package com.bigdata.practice1011; // 不保留父类的泛型 // 父类的泛型显示 // 泛型擦除:实现或继承父类的子类没有指定类型 类似于Object【不等于Object,<Object>编译不通过】 public class FanXin3 extends FanXin{ @Override public void setName(Object sex) { } } class FanXin4 extends FanXin<String, Integer>{ @Override public void setName(Integer sex) { } }
-
-
子类重写父类的方法,泛型类型随父类而定 子类使用父类的属性,该属性类型随父类定义的泛型
通配符
T、K、V、E
等泛型字母为有类型,类型参数赋予具体的值?
未知类型 类型参数赋予不确定值,任意类型- 只能用在声明类型、方法参数上,不能用在定义泛型类上
泛型上下界:
上界 <= 【extends】
- 语法:
? extends List
- 范围:指定的类必须是继承某个类,或者实现了某个接口(不是implements),即<=
- 注意:一般用于限制操作 不能使用在添加数据上,一般都是用于数据的读取
下界 >= 【super】
- 语法:
? super List
- 范围: 即父类或本身
- 注意:一般用于下限操作
大概阅读源码理解一下就好了!
泛型嵌套
HashMap使用了泛型的嵌套
Map<String, String> map = new HashMap<String,String>();
map.put("a", "张三");
map.put("b", "李四");
Set<Entry<String, String>> set = map.entrySet();
for (Entry<String, String> entry : set) {
System.out.println(entry.getKey()+":"+entry.getValue());
}
注意:
static和泛型:
静态方法有一种情况需要注意一下,那就是在类中的静态方法使用泛型:静态方法无法访问类上定义的泛型;如果静态方法操作的引用数据类型不确定的时候,必须要将泛型定义在方法上。即使该类已经定义为泛型类。
// 泛型参数T2其实没用 静态方法的泛型T2和类定义的泛型T2没有任何关系
public class FanXin <T1, T2>{
T1 name;
public static <T2> T2 get(T2 name){
T2 t2 = name;
return t2;
};
}
泛型和数组
不能创建一个确切的泛型类型的数组,使用通配符创建泛型数组是可以的
List<String>[] ls = new ArrayList<String>[10]; // 不行
List<?>[] ls = new ArrayList<?>[10]; // 可以
List<String>[] ls = new ArrayList[10]; // 可以
泛型没有多态,泛型没有数组
枚举类
定义理解
概念:
JDK1.5引入的一种新类型枚举(Enum),是指一组固定常量组成的类型。其实是一种类型,是java.lang.Enum的子类。
如果学过单例模式,我们可以理解为枚举就是多例模式,一个类有多个实例,但实例个数不是越多越好的,是有限的。
语法:
修饰符 enum 枚举名{
枚举常量值1;
枚举常量值2;
}
// 调用
枚举名.枚举常量值1;
注意:
enum
是关键字,小写的 ;- 枚举型其实就是本类的实例;
- 多个枚举项之间使用逗号分隔,最后一个枚举项需要给出分号!如果只有枚举项(没有构造器,方法,实例变量)则可以省略分号。
- 不可使用
new
来创建枚举类对象,枚举类的枚举项就是实例,所以在外面用类面点来调用
使用
通常用枚举表示一组常用且个数有限的值,用于实现对输入值进行约束检查
内部类的形式去给到性别属性的控制:
package com.bigdata.practice1008;
/**
* 泛型 将对象类型作为参数 指定到其它类或者方法上
* 多个泛型参数用逗号隔开 整体用一对尖括号括起来
* @param <T>
* 1.泛型类
* 2.泛型属性
* 3.泛型方法
* 4.泛型参数
* 5.泛型返回值
*
*/
public class Person<T> {
private String name;
private int age;
private sexType sex;
private T personType;
public Person() {
}
enum sexType{
男,女
}
public Person(String name, int age, sexType sex, T personType) {
this.name = name;
this.age = age;
this.sex = sex;
this.personType = personType;
}
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 sexType getSex() {
return sex;
}
public void setSex(sexType sex) {
this.sex = sex;
}
public T getPersonType() {
return personType;
}
public void setPersonType(T personType) {
this.personType = personType;
}
public static <T> void printInfo(T st){
System.out.println("泛型类中的静态方法!");
if (st instanceof Student){
System.out.println("OK!");
}
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", sex=" + sex +
", personType=" + personType +
'}';
}
}
枚举和switch
说明
switch
中不可使用枚举类名称- 编译器会根据
switch(s)
中的s来确定每个枚举类型 case
中直接使用枚举项
示例
public enum Signal{
RED, GREEN, BLANK, YELLOW;
}
public class TrafficLight {
Signal color = Signal.RED;
public void change() {
switch (color) {
case RED:
color = Signal.GREEN;
break;
case YELLOW:
color = Signal.RED;
break;
case GREEN:
color = Signal.YELLOW;
break;
}
}
}
java.lang.Enum
Java 中的每一个枚举都继承自 java.lang.Enum 类。当定义一个枚举类型时,每一个枚举类型成员都可以看作是 Enum 类的实例,这些枚举成员默认都被 final、public, static 修饰,当使用枚举类型成员时,直接使用枚举名称调用成员即可
方法名称 | 描述 |
---|---|
values() | 以数组形式返回枚举类型的所有成员 |
valueOf() | 将普通字符串转换为枚举实例 |
compareTo() | 比较两个枚举成员在定义时的顺序 |
ordinal() | 获取枚举成员的索引位置 |
枚举类
- 可以拥有自己的方法
- 可以拥有自己的构造
EnumMap 与 EnumSet
为了更好地支持枚举类型,java.util 中添加了两个新类:EnumMap 和 EnumSet。使用它们可以更高效地操作枚举类型。
EnumMap 类
EnumMap 是专门为枚举类型量身定做的 Map 实现。虽然使用其他的 Map(如 HashMap)实现也能完成枚举类型实例到值的映射,但是使用 EnumMap 会更加高效。
HashMap 只能接收同一枚举类型的实例作为键值,并且由于枚举类型实例的数量相对固定并且有限,所以 EnumMap 使用数组来存放与枚举类型对应的值,使得 EnumMap 的效率非常高。
EnumSet
- EnumSet 是枚举类型的高性能 Set 实现,它要求放入它的枚举常量必须属于同一枚举类型。EnumSet 提供了许多工厂方法以便于初始化。
- allOf(Class element type) 创建一个包含指定枚举类型中所有枚举成员的 EnumSet 对象
- complementOf(EnumSet s) 创建一个与指定 EnumSet 对象 s 相同的枚举类型 EnumSet 对象,并包含所有 s 中未包含的枚举成员
- copyOf(EnumSet s) 创建一个与指定 EnumSet 对象 s 相同的枚举类型 EnumSet 对象,并与 s 包含相同的枚举成员
- noneOf(<Class elementType) 创建指定枚举类型的空 EnumSet 对象
- of(E first,e…rest) 创建包含指定枚举成员的 EnumSet 对象
- range(E from ,E to) 创建一个 EnumSet 对象,该对象包含了 from 到 to 之间的所有枚举成员
静态导入
理解
在一个类中使用其它类的静态属性和静态方法时,需要静态导入。
使用
直接在前面用 import static 包名;
导入,或者类名点去直接调用
不推荐使用,有的时候同包不同类的同名函数或者同名参数不太好处理
lambda表达式
简介
概念:
lambda,其实是数学符号中的 λ,一个希腊字母。拉姆达 Lambda(大写Λ,小写λ),是第十一个希腊字母;在计算机术语中,Lambda 多表达式”是一个匿名函数,可以包含表达式和语句,并且可用于创建委托或表达式目录树类型。Java 8 引入的 Lambda 表达式的主要作用就是简化部分的写法。
理解:
- Lambda表达式本身就是一个接口的实现
- 把“一块代码”赋给了一个变量。而“这块代码”,或者说“这个被赋给一个变量的函数”,就是一个Lambda表达式。
以Lambda语法创建线程和匿名内部类创建线程的区别:
@FunctionalInterface
public interface Runnable {
/**
* When an object implementing interface <code>Runnable</code> is used
* to create a thread, starting the thread causes the object's
* <code>run</code> method to be called in that separately executing
* thread.
* <p>
* The general contract of the method <code>run</code> is that it may
* take any action whatsoever.
*
* @see java.lang.Thread#run()
*/
public abstract void run();
}
public static void main(String[] args) {
// 用匿名内部类的方式来创建线程
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("我是一个线程啊");
}
});
// 使用Lambda来创建线程
new Thread(() -> System.out.println("我是一个线程啊"));
}
使用
接口的实现
- 1.new实现类
- 2.new接口自己匿名类借助
- 3.lambda表达式【只能去实现一个抽象方法的接口,用
@FunctionalInterface
注解的接口】
语法
-
语法:
(方法参数) -> {方法要实现的内容}
-
1.语法格式一:无参,无返回值,Lambda 体只需一条语句。
示例:
Runnable r1 = () -> System.out.println("Hello Lambda!");
2.语法格式二:Lambda 需要一个参数。
示例:
Consumer<String> con = (x) -> System.out.println(x);
3.语法格式三:Lambda 只需要一个参数时,参数的小括号可以省略。
示例:
Consumer<String> con = x -> System.out.println(x);
4.语法格式四:Lambda 需要两个参数,并且有返回值。
示例:
Comparator<Integer> com = (x, y) -> { System.out.println("函数式接口"); return Integer.compare(x, y); };
5.语法格式五:当 Lambda 体只有一条语句时,return 与大括号可以省略。
示例:
Comparator<Integer> com = (x, y) -> Integer.compare(x, y);
6.Lambda的表达式的参数不用去显示声明类型,jvm ”上下文推断出“类型
-
使用Labmda表达式需要函数式编程接口,接口上加上
@FunctionalInterface
注解(标记着这个接口只有一个抽象方法)
函数式编程接口
接口上加上 @FunctionalInterface
注解(标记着这个接口只有一个抽象方法)
内部的函数式编程接口的学习和Stream流一起
lambda表达式的特征
- 可选的类型声明:不需要声明参数类型,编译器可以自动识别参数类型。
- 可选的参数圆括号:一个参数无须使用圆括号,但多个参数则需要使用圆括号。
- 可选的花括号:如果函数体只有一条语句,则不需要使用花括号。
- 可选的返回关键字:如果函数体只有一条语句,则编译器会自动返回该语句执行的结果;如果有多条语句且有返回值,那么在花括号中需要使用return语句来返回结果。
Lambda结合FunctionalInterface Lib, forEach, stream(),method reference等新特性可以使代码变的更加简洁!后面再来
可变参数:
定义
- A、 可变参数:适用于参数个数不确定,类型确定的情况,java把可变参数当做数组处理。
- B、 注意:可变参数必须位于最后一项。
特点
- A、 只能出现在参数列表的最后;
- B、 …位于变量类型和变量名之间,前后有无空格都可以;
- C、 调用可变参数的方法时,编译器为该可变参数隐含创建一个数组,在方法体中以数组的形式访问可变参数。
package com.bigdata.practice1011;
public class ChangeParam {
public static void main(String[] args) {
append(25, new String[] {"AA", "Cc"});
append(34, "AA", "BB");
}
/**
* 可变参数:
* 必须写在参数列表最后,
* 可以当作数组使用
* 传参是可以是显示声明数组传入参数,也可以是单个单个传入
* ... 和参数类型与参数变量之间可以有空格可以没有空格
*/
public static void append(int a, String... strs) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < strs.length; i++) {
System.out.println(strs[i]);
sb.append(strs[i]);
}
System.out.println(sb.toString());
}
}
调用可变参数的方法时,编译器为该可变参数隐含创建一个数组,在方法体中以数组的形式访问可变参数。
package com.bigdata.practice1011;
public class ChangeParam {
public static void main(String[] args) {
append(25, new String[] {"AA", "Cc"});
append(34, "AA", "BB");
}
/**
* 可变参数:
* 必须写在参数列表最后,
* 可以当作数组使用
* 传参是可以是显示声明数组传入参数,也可以是单个单个传入
* ... 和参数类型与参数变量之间可以有空格可以没有空格
*/
public static void append(int a, String... strs) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < strs.length; i++) {
System.out.println(strs[i]);
sb.append(strs[i]);
}
System.out.println(sb.toString());
}
}