【转载】见过的最详细明了的泛型讲解

@Java泛型详解:和Class的使用。泛型类,泛型方法的详细使用实例
TOC
一、引入
1、泛型是什么
首先告诉大家ArrayList就是泛型。那ArrayList能完成哪些想不到的功能呢?先看看下面这段代码:
[java] view plain copy
ArrayList strList = new ArrayList();
ArrayList intList = new ArrayList();
ArrayList doubleList = new ArrayList();
大家对ArrayList很熟悉,这里构造了三个List,分别盛装String、Integer和Double;这就是ArrayList的过人之处:即各种类型的变量都可以组装成对应的List,而不必针对每个类型分别实现一个构建ArrayList的类。这里可能看不懂,开篇总是困难的,下面看看如果没有泛型的话,我们要怎么做;
2、没有泛型会怎样
先看下面这段代码:
我们实现两个能够设置点坐标的类,分别设置Integer类型的点坐标和Float类型的点坐标:
[java] view plain copy
//设置Integer类型的点坐标
class IntegerPoint{
private Integer x ; // 表示X坐标
private Integer y ; // 表示Y坐标
public void setX(Integer x){
this.x = x ;
}
public void setY(Integer y){
this.y = y ;
}
public Integer getX(){
return this.x ;
}
public Integer getY(){
return this.y ;
}
}
//设置Float类型的点坐标
class FloatPoint{
private Float x ; // 表示X坐标
private Float y ; // 表示Y坐标
public void setX(Float x){
this.x = x ;
}
public void setY(Float y){
this.y = y ;
}
public Float getX(){
return this.x ;
}
public Float getY(){
return this.y ;
}
}
那现在有个问题:大家有没有发现,他们除了变量类型不一样,一个是Integer一个是Float以外,其它并没有什么区别!那我们能不能合并成一个呢?
答案是可以的,因为Integer和Float都是派生自Object的,我们用下面这段代码代替:
[java] view plain copy
class ObjectPoint{
private Object x ;
private Object y ;
public void setX(Object x){
this.x = x ;
}
public void setY(Object y){
this.y = y ;
}
public Object getX(){
return this.x ;
}
public Object getY(){
return this.y ;
}
}
即全部都用Object来代替所有的子类;
在使用的时候是这样的:
[java] view plain copy
ObjectPoint integerPoint = new ObjectPoint();
integerPoint.setX(new Integer(100));
Integer integerX=(Integer)integerPoint.getX();
在设置的时候,使用new Integer(100)来新建一个Integer
[java] view plain copy
integerPoint.setX(new Integer(100));
然后在取值的时候,进行强制转换:
[java] view plain copy
Integer integerX=(Integer)integerPoint.getX();
由于我们设置的时候,是设置的Integer,所以在取值的时候,强制转换是不会出错的。
同理,FloatPoint的设置和取值也是类似的,代码如下:
[java] view plain copy
ObjectPoint floatPoint = new ObjectPoint();
floatPoint.setX(new Float(100.12f));
Float floatX = (Float)floatPoint.getX();
但问题来了:注意,注意,我们这里使用了强制转换,我们这里setX()和getX()写得很近,所以我们明确的知道我们传进去的是Float类型,那如果我们记错了呢?
比如我们改成下面这样,编译时会报错吗:
[java] view plain copy
ObjectPoint floatPoint = new ObjectPoint();
floatPoint.setX(new Float(100.12f));
String floatX = (String)floatPoint.getX();
不会!!!我们问题的关键在于这句:
[java] view plain copy
String floatX = (String)floatPoint.getX();
强制转换时,会不会出错。因为编译器也不知道你传进去的是什么,而floatPoint.getX()返回的类型是Object,所以编译时,将Object强转成String是成立的。必然不会报错。
而在运行时,则不然,在运行时,floatPoint实例中明明传进去的是Float类型的变量,非要把它强转成String类型,肯定会报类型转换错误的!
那有没有一种办法在编译阶段,即能合并成同一个,又能在编译时检查出来传进去类型不对呢?当然,这就是泛型。
下面我们将对泛型的写法和用法做一一讲解。
二、各种泛型定义及使用
1、泛型类定义及使用
我们先看看泛型的类是怎么定义的:
[java] view plain copy
//定义
class Point{// 此处可以随便写标识符号
private T x ;
private T y ;
public void setX(T x){//作为参数
this.x = x ;
}
public void setY(T y){
this.y = y ;
}
public T getX(){//作为返回值
return this.x ;
}
public T getY(){
return this.y ;
}
};
//IntegerPoint使用
Point p = new Point() ;
p.setX(new Integer(100)) ;
System.out.println(p.getX());

//FloatPoint使用
Point p = new Point() ;
p.setX(new Float(100.12f)) ;
System.out.println(p.getX());
先看看运行结果:

从结果中可以看到,我们实现了开篇中IntegerPoint类和FloatPoint类的效果。下面来看看泛型是怎么定义及使用的吧。
(1)、定义泛型:Point
首先,大家可以看到Point,即在类名后面加一个尖括号,括号里是一个大写字母。这里写的是T,其实这个字母可以是任何大写字母,大家这里先记着,可以是任何大写字母,意义是相同的。
(2)类中使用泛型
这个T表示派生自Object类的任何类,比如String,Integer,Double等等。这里要注意的是,T一定是派生于Object类的。为方便起见,大家可以在这里把T当成String,即String在类中怎么用,那T在类中就可以怎么用!所以下面的:定义变量,作为返回值,作为参数传入的定义就很容易理解了。
[java] view plain copy
//定义变量
private T x ;
//作为返回值
public T getX(){
return x ;
}
//作为参数
public void setX(T x){
this.x = x ;
}
(3)使用泛型类
下面是泛型类的用法:
[java] view plain copy
//IntegerPoint使用
Point p = new Point() ;
p.setX(new Integer(100)) ;
System.out.println(p.getX());

//FloatPoint使用
Point p = new Point() ;
p.setX(new Float(100.12f)) ;
System.out.println(p.getX());
首先,是构造一个实例:
[java] view plain copy
Point p = new Point() ;
这里与普通构造类实例的不同之点在于,普通类构造函数是这样的:Point p = new Point() ;
而泛型类的构造则需要在类名后添加上,即一对尖括号,中间写上要传入的类型。
因为我们构造时,是这样的:class Point,所以在使用的时候也要在Point后加上类型来定义T代表的意义。
然后在getVar()和setVar()时就没有什么特殊的了,直接调用即可。
从上面的使用时,明显可以看出泛型的作用,在构造泛型类的实例的时候:
[java] view plain copy
//IntegerPoint使用
Point p = new Point() ;
//FloatPoint使用
Point p = new Point() ;
尖括号中,你传进去的是什么,T就代表什么类型。这就是泛型的最大作用,我们只需要考虑逻辑实现,就能拿给各种类来用。
前面我们提到ArrayList也是泛型,我们顺便它的实现:
[java] view plain copy
public class ArrayList{
…………
}
看到了吧,跟我们的Point实现是一样的,这也就是为什么ArrayList能够盛装各种类型的主要原因。
(4)使用泛型实现的优势
相比我们开篇时使用Object的方式,有两个优点:
(1)、不用强制转换
[java] view plain copy
//使用Object作为返回值,要强制转换成指定类型
Float floatX = (Float)floatPoint.getX();
//使用泛型时,不用强制转换,直接出来就是String
System.out.println(p.getVar());
(2)、在settVar()时如果传入类型不对,编译时会报错

可以看到,当我们构造时使用的是String,而在setVar时,传进去Integer类型时,就会报错。而不是像Object实现方式一样,在运行时才会报强制转换错误。
2、多泛型变量定义及字母规范
(1)、多泛型变量定义
上在我们只定义了一个泛型变量T,那如果我们需要传进去多个泛型要怎么办呢?
只需要在类似下面这样就可以了:
[java] view plain copy
class MorePoint<T,U>{
}
也就是在原来的T后面用逗号隔开,写上其它的任意大写字母即可。想加几个就加几个,比如我们想加五个泛型变量,那应该是这样的:
[java] view plain copy
class MorePoint<T,U,A,B,C>{
}
举个粟子,我们在Point上再另加一个字段name,也用泛型来表示,那要怎么做?代码如下:
[java] view plain copy
class MorePoint<T,U> {
private T x;
private T y;

private U name;  

public void setX(T x) {  
    this.x = x;  
}  
public T getX() {  
    return this.x;  
}  
…………  
public void setName(U name){  
    this.name = name;  
}  

public U getName() {  
    return this.name;  
}  

}
//使用
MorePoint<Integer,String> morePoint = new MorePoint<Integer, String>();
morePoint.setName(“harvic”);
Log.d(TAG, “morPont.getName:” + morePoint.getName());
从上面的代码中,可以明显看出,就是在新添加的泛型变量U用法与T是一样的。
(2)、字母规范
在定义泛型类时,我们已经提到用于指定泛型的变量是一个大写字母:
[java] view plain copy
class Point{
…………
}
当然不是的!!!!任意一个大写字母都可以。他们的意义是完全相同的,但为了提高可读性,大家还是用有意义的字母比较好,一般来讲,在不同的情境下使用的字母意义如下:
E — Element,常用在java Collection里,如:List,Iterator,Set
K,V — Key,Value,代表Map的键值对
N — Number,数字
T — Type,类型,如String,Integer等等
如果这些还不够用,那就自己随便取吧,反正26个英文字母呢。
再重复一遍,使用哪个字母是没有特定意义的!只是为了提高可读性!!!!
3、泛型接口定义及使用
在接口上定义泛型与在类中定义泛型是一样的,代码如下:

[java] view plain copy
interface Info{ // 在接口上定义泛型
public T getVar() ; // 定义抽象方法,抽象方法的返回值就是泛型类型
public void setVar(T x);
}
与泛型类的定义一样,也是在接口名后加尖括号;
(1)、使用方法一:非泛型类
但是在使用的时候,就出现问题了,我们先看看下面这个使用方法:
[java] view plain copy
class InfoImpl implements Info{ // 定义泛型接口的子类
private String var ; // 定义属性
public InfoImpl(String var){ // 通过构造方法设置属性内容
this.setVar(var) ;
}
@Override
public void setVar(String var){
this.var = var ;
}
@Override
public String getVar(){
return this.var ;
}
}

public class GenericsDemo24{
public void main(String arsg[]){
InfoImpl i = new InfoImpl(“harvic”);
System.out.println(i.getVar()) ;
}
};
首先,先看InfoImpl的定义:
[java] view plain copy
class InfoImpl implements Info{
…………
}
要清楚的一点是InfoImpl不是一个泛型类!因为他类名后没有!
然后在在这里我们将Info中的泛型变量T定义填充为了String类型。所以在重写时setVar()和getVar()时,IDE会也我们直接生成String类型的重写函数。
最后在使用时,没什么难度,传进去String类型的字符串来构造InfoImpl实例,然后调用它的函数即可。
[java] view plain copy
public class GenericsDemo24{
public void main(String arsg[]){
InfoImpl i = new InfoImpl(“harvic”);
System.out.println(i.getVar()) ;
}
};
(2)、使用方法二:泛型类
在方法一中,我们在类中直接把Info接口给填充好了,但我们的类,是可以构造成泛型类的,那我们利用泛型类来构造填充泛型接口会是怎样呢?

[java] view plain copy
interface Info{ // 在接口上定义泛型
public T getVar() ; // 定义抽象方法,抽象方法的返回值就是泛型类型
public void setVar(T var);
}
class InfoImpl implements Info{ // 定义泛型接口的子类
private T var ; // 定义属性
public InfoImpl(T var){ // 通过构造方法设置属性内容
this.setVar(var) ;
}
public void setVar(T var){
this.var = var ;
}
public T getVar(){
return this.var ;
}
}
public class GenericsDemo24{
public static void main(String arsg[]){
InfoImpl i = new InfoImpl(“harvic”);
System.out.println(i.getVar()) ;
}
};
最关键的是构造泛型类的过程:
[java] view plain copy
class InfoImpl implements Info{ // 定义泛型接口的子类
private T var ; // 定义属性
public InfoImpl(T var){ // 通过构造方法设置属性内容
this.setVar(var) ;
}
public void setVar(T var){
this.var = var ;
}
public T getVar(){
return this.var ;
}
}
在这个类中,我们构造了一个泛型类InfoImpl,然后把泛型变量T传给了Info,这说明接口和泛型类使用的都是同一个泛型变量。
然后在使用时,就是构造一个泛型类的实例的过程,使用过程也不变。
[java] view plain copy
public class GenericsDemo24{
public static void main(String arsg[]){
Info i = new InfoImpl(“harvic”);
System.out.println(i.getVar()) ;
}
};
使用泛型类来继承泛型接口的作用就是让用户来定义接口所使用的变量类型,而不是像方法一那样,在类中写死。
那我们稍微加深点难度,构造一个多个泛型变量的类,并继承自Info接口:
[java] view plain copy
class InfoImpl<T,K,U> implements Info{ // 定义泛型接口的子类
private U var ;
private T x;
private K y;
public InfoImpl(U var){ // 通过构造方法设置属性内容
this.setVar(var) ;
}
public void setVar(U var){
this.var = var ;
}
public U getVar(){
return this.var ;
}
}
在这个例子中,我们在泛型类中定义三个泛型变量T,K,U并且把第三个泛型变量U用来填充接口Info。所以在这个例子中Info所使用的类型就是由U来决定的。
使用时是这样的:泛型类的基本用法,不再多讲,代码如下:
[java] view plain copy
public class GenericsDemo24{
public void main(String arsg[]){
InfoImpl<Integer,Double,String> i = new InfoImpl<Integer,Double,String>(“harvic”);
System.out.println(i.getVar()) ;
}
}
4、泛型函数定义及使用
上面我们讲解了类和接口的泛型使用,下面我们再说说,怎么单独在一个函数里使用泛型。比如我们在新建一个普通的类StaticFans,然后在其中定义了两个泛型函数:
[java] view plain copy
public class StaticFans {
//静态函数
public static void StaticMethod(T a){
Log.d(“harvic”,"StaticMethod: "+a.toString());
}
//普通函数
public void OtherMethod(T a){
Log.d(“harvic”,"OtherMethod: "+a.toString());
}
}
上面分别是静态泛型函数和常规泛型函数的定义方法,与以往方法的唯一不同点就是在返回值前加上来表示泛型变量。其它没什么区别。
使用方法如下:
[java] view plain copy
//静态方法
StaticFans.StaticMethod(“adfdsa”);//使用方法一
StaticFans.StaticMethod(“adfdsa”);//使用方法二

//常规方法
StaticFans staticFans = new StaticFans();
staticFans.OtherMethod(new Integer(123));//使用方法一
staticFans.OtherMethod(new Integer(123));//使用方法二
结果如下:

首先,我们看静态泛型函数的使用方法:
[java] view plain copy
StaticFans.StaticMethod(“adfdsa”);//使用方法一
StaticFans.StaticMethod(“adfdsa”);//使用方法二
从结果中我们可以看到,这两种方法的结果是完全一样的,但他们还有些区别的,区别如下:
方法一,可以像普通方法一样,直接传值,任何值都可以(但必须是派生自Object类的类型,比如String,Integer等),函数会在内部根据传进去的参数来识别当前T的类别。但尽量不要使用这种隐式的传递方式,代码不利于阅读和维护。因为从外观根本看不出来你调用的是一个泛型函数。
方法二,与方法一不同的地方在于,在调用方法前加了一个来指定传给的值,如果加了这个来指定参数的值的话,那StaticMethod()函数里所有用到的T类型也就是强制指定了是String类型。这是我们建议使用的方式。
同样,常规泛型函数的使用也有这两种方式:
[java] view plain copy
StaticFans staticFans = new StaticFans();
staticFans.OtherMethod(new Integer(123));//使用方法一
staticFans.OtherMethod(new Integer(123));//使用方法二
可以看到,与平常一样,先创建类的实例,然后调用泛型函数。
方法一,隐式传递了T的类型,与上面一样,不建议这么做。
方法二,显示将T赋值为Integer类型,这样OtherMethod(T a)传递过来的参数如果不是Integer那么编译器就会报错。

进阶:返回值中存在泛型
上面我们的函数中,返回值都是void,但现实中不可能都是void,有时,我们需要将泛型变量返回,比如下面这个函数:
[java] view plain copy
public static List parseArray(String response,Class object){
List modelList = JSON.parseArray(response, object);
return modelList;
}
函数返回值是List类型。至于传入参数Class object的意义,我们下面会讲。这里也就是想通过这个例子来告诉大家,泛型变量其实跟String,Integer,Double等等的类的使用上没有任何区别,T只是一个符号,可以代表String,Integer,Double……这些类的符号,在泛型函数使用时,直接把T看到String,Integer,Double……中的任一个来写代码就可以了。唯一不同的是,要在函数定义的中在返回值前加上标识泛型;
5、其它用法:Class类传递及泛型数组
(1)、使用Class传递泛型类Class对象
有时,我们会遇到一个情况,比如,我们在使用JSON解析字符串的时候,代码一般是这样的
[java] view plain copy
public static List parseArray(String response){
List modelList = JSON.parseArray(response, SuccessModel.class);
return modelList;
}
其中SuccessModel是自定义的解析类,代码如下,其实大家不用管SuccessModel的定义,只考虑上面的那段代码就行了。写出来SuccessModel的代码,只是不想大家感到迷惑,其实,这里只是fastJson的基本用法而已。
这段代码的意义就是根据SuccessModel解析出List的数组。
[java] view plain copy
public class SuccessModel {
private boolean success;

public boolean isSuccess() {  
    return success;  
}  

public void setSuccess(boolean success) {  
    this.success = success;  
}  

}
那现在,我们把下面这句组装成一个泛型函数要怎么来做呢?
[java] view plain copy
public static List parseArray(String response){
List modelList = JSON.parseArray(response, SuccessModel.class);
return modelList;
}
首先,我们应该把SuccessModel单独抽出来做为泛型变量,但parseArray()中用到的SuccessModel.class要怎么弄呢?
先来看代码:
[java] view plain copy
public static List parseArray(String response,Class object){
List modelList = JSON.parseArray(response, object);
return modelList;
}
注意到,我们用的Class object来传递类的class对象,即我们上面提到的SuccessModel.class。
这是因为Class也是一泛型,它是传来用来装载类的class对象的,它的定义如下:
[java] view plain copy
public final class Class implements Serializable {
…………
}
通过Class来加载泛型的Class对象的问题就讲完了,下面来看看泛型数组的使用方法吧。
(2)、定义泛型数组
在写程序时,大家可能会遇到类似String[] list = new String[8];的需求,这里可以定义String数组,当然我们也可以定义泛型数组,泛型数组的定义方法为 T[],与String[]是一致的,下面看看用法:
[java] view plain copy
//定义
public static T[] fun1(T…arg){ // 接收可变参数
return arg ; // 返回泛型数组
}
//使用
public static void main(String args[]){
Integer i[] = fun1(1,2,3,4,5,6) ;
Integer[] result = fun1(i) ;
}
我们先看看 定义时的代码:
[java] view plain copy
public static T[] fun1(T…arg){ // 接收可变参数
return arg ; // 返回泛型数组
}
首先,定义了一个静态函数,然后定义返回值为T[],参数为接收的T类型的可变长参数。如果有同学对T…arg的用法不了解,可以去找下JAVA 可变长参数方面的知识。
由于可变长参数在输入后,会保存在arg这个数组中,所以,我们直接把数组返回即可。

转自:http://blog.csdn.net/harvic880925/article/details/49872903

下面是我自己实际使用泛型的几个实例。
希望看完上面的文章之后,再看实际使用的例子,可以更好的理解和使用和实际使用。

关于泛型类的使用实例
import lombok.Data;

@Data
public class MultiObject {

/**
 * 成功状态
 */
private boolean success;

/**
 * 异常
 */
private Exception ex;

/**
 * 数据
 */
private T obj;

public MultiObject() {
}

/**
 * 注意:当传入的泛型是Boolean时,就和第三个构造函数冲突了。
 */
public MultiObject(boolean success) {
	this.success = success;
}

public MultiObject(Exception ex) {
	this.success = false;
	this.ex = ex;
}

public MultiObject(T value) {
	this.success = true;
	this.obj = value;
}

}

简单解释下这个model。
在实际业务代码里面,可能有很多种操作,然后我们关心这个操作的执行结果,主要有几点。
1,成功与否。对应属性success。
2,异常信息。对应属性ex。若是操作正常执行,则就不在意这个属性的值。
3,我们操作的最终目的对象。对应属性obj。

泛型牛逼的地方就是在这个地方。
如果你不用泛型,而使用Object类型,那么每次执行完之后,我们即使得到这个结果,还得类型转换一下,那么这下就像文章上面描述的那样。分分钟出现castfailexception。也就是类型转换异常啦。
但是,若是使用了这个泛型之后,那么我们的某个操作所需要的返回结果是什么类型,就可以 传入什么类型,而且在实际取得返回结果的时候,就不需要使用类型转换,这样就很好的达到了目的。
这个主要是代码设计层次的提高。写再多的业务代码,要是不提高,那么写的都是渣。

关于,这个model代码里面为啥没有getter和setter,都是因为使用@Data这个注解,可以自动填充这个getter和setter。所以。就表在意这个问题啦。在其他地方可以正常使用各个属性getter和setter方法,虽然这些方法,你暂时看不见。有兴趣的可以了解下lombok。

关于泛型方法的使用实例
这个地方就有2个,但是上面文章也都讲到啦。
1,一个是泛型表示某一个类型的参数。为的传递某一类的参数对象
2,另一个则是传递的不是参数,而是代表Class,某一个类。

恰巧我都使用过,就正好记录一下实际使用实例。

/**
 * 将Json字符串信息转换成对应的Java对象
 *
 * @param json json字符串对象
 * @param c    对应的类型
 */
public static <T> T parseJsonToObj(String json, Class<T> c) {
    try {
        JSONObject jsonObject = JSONObject.parseObject(json);
        return JSON.toJavaObject(jsonObject, c);
    } catch (Exception e) {
        LOG.error(e.getMessage());
    }
    return null;
}

然后是具体调用的地方的代码。
Collector collectorObj = JSONUtils.parseJsonToObj(collector, Collector.class);
Flume flume = JSONUtils.parseJsonToObj(flumeJson, Flume.class);
Probe probe = JSONUtils.parseJsonToObj(probeJson, Probe.class);
可以看到,真的只是因为传入的参数类型不一样,但若你不知道泛型的话,那你就得没遇到一个类型的转换,你就得写一个这么个方法。

/**
 * @param dest   目的集合
 * @param source 源集合
 * @param <T>    集合参数的类型
 */
private static <T> void listAddAllAvoidNPE(List<T> dest, List<T> source) {
    if (source == null) {
        return;
    }
    dest.addAll(source);
}

private static <T> void listAddAvoidNull(List<T> dest, T source) {
    if (source == null) {
        return;
    }
    dest.add(source);
}

这个就是传入的参数为某一类的参数,主要是要使用参数对象,而不是上面的那个使用的参数的类Class
我这方法提出来,主要是因为,直接使用list类的addAll()方法,如果添加的是null,那么就会抛异常。但是总不能我在所有使用的地方,都判断一下我要添加的参数是不是null,然后再调用list的addAll()方法吧。那样的话,这样的判断代码会啰嗦的海了去了。所以,就这么提出来了。
这个时候,这个T,使用起来就像使用我们常用的一般对象一样,我这的参数是个List类型,当然也可是其他类型的,姿势都一样。

然后是具体调用的地方的代码

List<ProbeObject> list = Lists.newArrayList();
listAddAllAvoidNPE(list, decoder.getProperties());

这个方法的第二个参数的返回值可能是null,所以,直接调用addAll(),就会抛空指针异常。所以,就如上,那么一提取。就好多啦。

欢迎使用Markdown编辑器

你好! 这是你第一次使用 Markdown编辑器 所展示的欢迎页。如果你想学习如何使用Markdown编辑器, 可以仔细阅读这篇文章,了解一下Markdown的基本语法知识。

新的改变

我们对Markdown编辑器进行了一些功能拓展与语法支持,除了标准的Markdown编辑器功能,我们增加了如下几点新功能,帮助你用它写博客:

  1. 全新的界面设计 ,将会带来全新的写作体验;
  2. 在创作中心设置你喜爱的代码高亮样式,Markdown 将代码片显示选择的高亮样式 进行展示;
  3. 增加了 图片拖拽 功能,你可以将本地的图片直接拖拽到编辑区域直接展示;
  4. 全新的 KaTeX数学公式 语法;
  5. 增加了支持甘特图的mermaid语法1 功能;
  6. 增加了 多屏幕编辑 Markdown文章功能;
  7. 增加了 焦点写作模式、预览模式、简洁写作模式、左右区域同步滚轮设置 等功能,功能按钮位于编辑区域与预览区域中间;
  8. 增加了 检查列表 功能。

功能快捷键

撤销:Ctrl/Command + Z
重做:Ctrl/Command + Y
加粗:Ctrl/Command + B
斜体:Ctrl/Command + I
标题:Ctrl/Command + Shift + H
无序列表:Ctrl/Command + Shift + U
有序列表:Ctrl/Command + Shift + O
检查列表:Ctrl/Command + Shift + C
插入代码:Ctrl/Command + Shift + K
插入链接:Ctrl/Command + Shift + L
插入图片:Ctrl/Command + Shift + G
查找:Ctrl/Command + F
替换:Ctrl/Command + G

合理的创建标题,有助于目录的生成

直接输入1次#,并按下space后,将生成1级标题。
输入2次#,并按下space后,将生成2级标题。
以此类推,我们支持6级标题。有助于使用TOC语法后生成一个完美的目录。

如何改变文本的样式

强调文本 强调文本

加粗文本 加粗文本

标记文本

删除文本

引用文本

H2O is是液体。

210 运算结果是 1024.

插入链接与图片

链接: link.

图片: Alt

带尺寸的图片: Alt

居中的图片: Alt

居中并且带尺寸的图片: Alt

当然,我们为了让用户更加便捷,我们增加了图片拖拽功能。

如何插入一段漂亮的代码片

博客设置页面,选择一款你喜欢的代码片高亮样式,下面展示同样高亮的 代码片.

// An highlighted block
var foo = 'bar';

生成一个适合你的列表

  • 项目
    • 项目
      • 项目
  1. 项目1
  2. 项目2
  3. 项目3
  • 计划任务
  • 完成任务

创建一个表格

一个简单的表格是这么创建的:

项目Value
电脑$1600
手机$12
导管$1

设定内容居中、居左、居右

使用:---------:居中
使用:----------居左
使用----------:居右

第一列第二列第三列
第一列文本居中第二列文本居右第三列文本居左

SmartyPants

SmartyPants将ASCII标点字符转换为“智能”印刷标点HTML实体。例如:

TYPEASCIIHTML
Single backticks'Isn't this fun?'‘Isn’t this fun?’
Quotes"Isn't this fun?"“Isn’t this fun?”
Dashes-- is en-dash, --- is em-dash– is en-dash, — is em-dash

创建一个自定义列表

Markdown
Text-to- HTML conversion tool
Authors
John
Luke

如何创建一个注脚

一个具有注脚的文本。2

注释也是必不可少的

Markdown将文本转换为 HTML

KaTeX数学公式

您可以使用渲染LaTeX数学表达式 KaTeX:

Gamma公式展示 Γ ( n ) = ( n − 1 ) ! ∀ n ∈ N \Gamma(n) = (n-1)!\quad\forall n\in\mathbb N Γ(n)=(n1)!nN 是通过欧拉积分

Γ ( z ) = ∫ 0 ∞ t z − 1 e − t d t   . \Gamma(z) = \int_0^\infty t^{z-1}e^{-t}dt\,. Γ(z)=0tz1etdt.

你可以找到更多关于的信息 LaTeX 数学表达式here.

新的甘特图功能,丰富你的文章

Mon 06 Mon 13 Mon 20 已完成 进行中 计划一 计划二 现有任务 Adding GANTT diagram functionality to mermaid
  • 关于 甘特图 语法,参考 这儿,

UML 图表

可以使用UML图表进行渲染。 Mermaid. 例如下面产生的一个序列图:

张三 李四 王五 你好!李四, 最近怎么样? 你最近怎么样,王五? 我很好,谢谢! 我很好,谢谢! 李四想了很长时间, 文字太长了 不适合放在一行. 打量着王五... 很好... 王五, 你怎么样? 张三 李四 王五

这将产生一个流程图。:

链接
长方形
圆角长方形
菱形
  • 关于 Mermaid 语法,参考 这儿,

FLowchart流程图

我们依旧会支持flowchart的流程图:

Created with Raphaël 2.2.0 开始 我的操作 确认? 结束 yes no
  • 关于 Flowchart流程图 语法,参考 这儿.

导出与导入

导出

如果你想尝试使用此编辑器, 你可以在此篇文章任意编辑。当你完成了一篇文章的写作, 在上方工具栏找到 文章导出 ,生成一个.md文件或者.html文件进行本地保存。

导入

如果你想加载一篇你写过的.md文件,在上方工具栏可以选择导入功能进行对应扩展名的文件导入,
继续你的创作。


  1. mermaid语法说明 ↩︎

  2. 注脚的解释 ↩︎

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java中的泛型是一种强类型机制,它可以让我们在编译时就可以检测到类型错误,从而避免在运行时发生类型错误。泛型主要通过类型参数来实现,可以在定义类、接口、方法时使用类型参数,这样这些类、接口、方法就可以处理不同类型的数据。 举例来说,假设我们需要定义一个可以存储任意类型数据的容器。我们可以使用泛型来实现: ```java public class Container<T> { private T data; public void setData(T data) { this.data = data; } public T getData() { return data; } } ``` 在上面的代码中,我们使用了一个类型参数T来定义容器中存储的数据类型。通过这个类型参数,我们就可以在定义容器时指定数据类型,例如: ```java Container<String> container1 = new Container<>(); container1.setData("Hello"); System.out.println(container1.getData()); // 输出:Hello Container<Integer> container2 = new Container<>(); container2.setData(123); System.out.println(container2.getData()); // 输出:123 ``` 在上面的代码中,我们分别定义了两个容器,一个存储字符串类型的数据,另一个存储整数类型的数据。由于使用了泛型,我们可以在编译时就检测出类型错误,从而避免在运行时发生类型错误。 除了在类中使用泛型,我们还可以在方法中使用泛型。例如,假设我们需要定义一个方法,可以将一个数组中的元素逆序排: ```java public static <T> void reverse(T[] array) { int i = 0; int j = array.length - 1; while (i < j) { T temp = array[i]; array[i] = array[j]; array[j] = temp; i++; j--; } } ``` 在上面的代码中,我们使用了一个类型参数T来定义数组中元素的类型。通过这个类型参数,我们就可以在调用方法时指定数组中元素的类型,例如: ```java Integer[] array1 = {1, 2, 3, 4, 5}; reverse(array1); System.out.println(Arrays.toString(array1)); // 输出:[5, 4, 3, 2, 1] String[] array2 = {"a", "b", "c", "d", "e"}; reverse(array2); System.out.println(Arrays.toString(array2)); // 输出:[e, d, c, b, a] ``` 在上面的代码中,我们分别定义了两个数组,一个存储整数类型的数据,另一个存储字符串类型的数据。由于使用了泛型,我们可以在编译时就检测出类型错误,从而避免在运行时发生类型错误。同时,我们可以在调用方法时指定数组中元素的类型,从而避免了类型转换的麻烦。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值