Java精选十大语法,快来瞅瞅!

开课吧开课吧锤锤2021-06-17 15:39

    日常开发中,我们经常使用泛型、自动拆箱和装箱、内部类、增强for循环、try-with-resources语法、lambda表达式等,因为这些特性可以帮助我们减少开发工作量;但是,我们从来没有认真研究过这些特性的本质是什么,所以这篇文章,就可以为您揭示这些特性背后的真相。

Java

    集合初始化

    集合的创建、赋值一步到位,想不想学?

    来,上边跟我一起画个List,在你下边画一个Map……

List<String> list = new ArrayList<String>() {{
    add("www.");
    add("javastack.");
    add("cn");
}};

Map<String, String> map = new HashMap<String, String>() {{
    put("1", "www.");
    put("2", "javastack.");
    put("3", "cn");
}};

   算术

static {
    final int size = -(-128) + 127 + 1;

    // Load and use the archived cache if it exists
    VM.initializeFromArchive(ByteCache.class);
    if (archivedCache == null || archivedCache.length != size) {
        Byte[] c = new Byte[size];
        byte value = (byte)-128;
        for(int i = 0; i < size; i++) {
            c[i] = new Byte(value++);
        }
        archivedCache = c;
    }
    cache = archivedCache;
}

    注意到上面size的写法没有?

    明明可以写成:

final int size = 256;

    他非要写成:

final int size = -(-128) + 127 + 1;

    这么花式的写法来自JDK包装类java.lang.Byte里面的静态方法。

    为什么要这么写呢?

    这样的写法在JDK里面有很多,大家看到这些写法都会觉得很奇怪。

    真正缘由无从考察,但栈长我觉得写JDK的大神其实就想告诉你,Byte的256个数是由-128~127这个范围组成的,起到一个标识数字范围的作用而已。

    移位

/**
 * The default initial capacity - MUST be a power of two.
 */
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16

/**
 * The maximum capacity, used if a higher value is implicitly specified
 * by either of the constructors with arguments.
 * MUST be a power of two <= 1<<30.
 */
static final int MAXIMUM_CAPACITY = 1 << 30;

    这两个变量来自java.util.HashMap源码,你可能也非常好奇为什么不直接写成数字,要弄一个移位“骚操作”?

    这是在告诉开发者,HashMap的容量大小必须是2的幂次,不然会造成空间浪费。

    复制变量

transient Collection<V> values;

public Collection<V> values() {
    Collection<V> vs = values;
    if (vs == null) {
        vs = new Values();
        values = vs;
    }
    return vs;
}

    以上同样来自java.util.HashMap的源码,为什么不直接用values:

transient Collection<V> values;

public Collection<V> values() {
    if (values == null) {
        values = new Values();
    }
    return values;
}

    而要重新定义一个vs来绕一个弯呢?

    这样写不是更简单么?

    JDK里面大量这样的写法,这是为什么呢?!

    那是因为操作局部变量要比读取全局变量要更快,另外,还有一个好处,再申明一下局部变量,可以很明显的看到这个变量的类型,而不要翻到上面或者用鼠标移上去来看变量类型。

    另外提一点,上面的复制变量再操作的方式让我想到了CopyOnWriteArrayList,这也是让当前变量不被其他线程改变保证当前线程变量一致性的一种方式。

    写JDK源码的都是大神啊,透过源码,我们能学到太多东西!

    泛型

    来看一段泛型的灵活运用:

public <R> Observable<R> compose(Transformer<? super T, ? extends R> transformer) {
    return ((Transformer<T, R>) transformer).call(this);
}

    这个泛型方法写得厉害吧,泛型T、R、通配符(?)、上边界(extends)和下边界(super)都用上了!

    常用的泛型含义:

    T-Type(类型)

    R-Result(结果)

    K-Key(键)

    V-Value(值)

    E-Element(元素)

    N-Number(数字)

    ?-不确定类型

    上面的泛型我们应该有常见到吧,边界和通配符不懂的可以看下这篇文章吧:困扰我多年的Java泛型和,终于搞清楚了。

    Lambda

    Lambda表达式这是Java8里面添加的新特性,用来简化匿名内部类以及结合函数式接口编程用的。

    如下面创建线程的示例:

// 1
Runnable runnable = () -> System.out.println("javastack.cn");
new Thread(runnable).start();

// 2
new Thread(() -> System.out.println("javastack.cn")).start();

// 3
new Thread(() -> clean()).start();

    三个不同的写法,我们再也不用写newRunnable()的一大堆的匿名内部类了,是不是很清爽了!

    如果你还不会用Lambda表达式,那真的OUT了。

    下面是一个Lambada真实案例:

@Bean
public CommandLineRunner commandLineRunner(NettyServer nettyServer) {
    return (args) -> {
        Thread thread = new Thread(() -> nettyServer.start());
        thread.setDaemon(true);
        thread.start();
    };
}

    上述示例省去了newCommandLineRunner的匿名内部类的过程。

    函数式编程

    上面有提到函数式编程,这是Java8里面添加的新特性,我之前在公众号里已经写过很多Java新特性的教程,这也不是新玩法了,已经被玩烂了。

    来看一个真实的案例,来自SpringBoot的邮件发送自动配置:

private void applyProperties(JavaMailSenderImpl sender) {
    PropertyMapper map = PropertyMapper.get();
    map.from(this.properties::getHost).to(sender::setHost);
    map.from(this.properties::getPort).whenNonNull().to(sender::setPort);
    map.from(this.properties::getUsername).to(sender::setUsername);
    map.from(this.properties::getPassword).to(sender::setPassword);
    map.from(this.properties::getProtocol).to(sender::setProtocol);
    map.from(this.properties::getDefaultEncoding).whenNonNull().as(Charset::name)
            .to(sender::setDefaultEncoding);
    map.from(this.properties::getProperties).whenNot(Map::isEmpty)
            .as(this::asProperties).to(sender::setJavaMailProperties);
}

    第一次看到这段代码的时候,我内心是拒绝的,很难理解。

    上面的from和to方法分别用到了Supplier和Consumer函数式接口,还用到了双冒号::结合使用,讳莫如深,还能结合Lambda表达式使用。

    函数式编程很厉害,虽然会用,但到现在我也觉得很高深,可读性和可理解性太差了,但是,看着厉害图片。

    流关闭

MyInputStream mis = new MyInputStream();
MyOutputStream mos = new MyOutputStream();
try (mis; mos) {
    mis.read("1.9");
    mos.write("1.9");
} catch (Exception e) {
    e.printStackTrace();
}

    没错,你看到的这个关闭流骚操作是Java9的新语法糖,较Java7又简化了try-with-resources用法,显摆的姿势越来越多了。

    关于try-with-resources的详细介绍及演进过程,

    类型推断

    Java10刚出来的时候,我写过两篇新特性文章:

    Java10的10个新特性,将彻底改变你写代码的方式!

    Java10实战第1篇:局部变量类型推断

    来,我再挑两个示例来欣赏下:

    示例1:

var javastack = "javastack";

    示例2

private static void testLoop() {
    for (var i = 0; i < 3; i++) {
        for (var m = 10; m < 15; m++) {
            System.out.println(i + m);
        }
    }
}

    类型推断出来后,都说Java越来越像Javascript了,其实就是Java10增加的一种语法糖而已,在编译期间会自动推断实际类型,其编译后的字节码和实际类型一致。

    模式匹配

    instanceof模式是匹配这是Java14推出来的新特性:

if (object instanceof Kid kid) {
    // ...
} else if (object instanceof Kiddle kiddle) {
    // ...
}

    匹配后直接创建对象和赋值直接拿来用,不需要再添加强制转换的代码,大大提高了可读性和安全性。具体可以看这篇文章:Java14之模式匹配,非常赞的一个新特性!

    以上就是开课吧小编为大家整理的“Java精选十大语法,快来瞅瞅!”一文,更多Java教程相关内容尽在开课吧广场Java教程频道!

免责声明:本站所提供的内容均来源于网友提供或网络搜集,由本站编辑整理,仅供个人研究、交流学习使用。如涉及版权问题,请联系本站管理员予以更改或删除。
有用
分享