Java基础系列之面向对象

2018-11-14  本文已影响0人  6bc9f71c8f0c

在初遇章节我们就谈到过Java是一门面向对象的语言,那么什么是面向对象呢?既然有面向对象语言,是否就有其他的语言?面向对象又能给我么带来什么好处呢?接下来,我们将在这个章节探讨下面向对象。

面向过程和面向对象


在目前的软件开发领域有两种主流的开发方法:结构化开发方法(面向过程)和面向对象开发方法。早期的编程语言C、Basic、Pascal等都是结构化编程语言,随着时代的变迁,软件的发展,人们发现了一种更好的可复用、可扩展和可维护的的方法,即面向对象,代表语言有C++,C#,Ruby,Java等。

面向对象的基本特征


抽象也是面向对象的重要组成之一,但是不是基本特征。抽象是抽取我们当前目标所需要的东西,排除一些无关的信息。

Java面向对象特征


在初遇章节我们就谈过Java的面向对象特征,我们这里再次谈谈Java面向对象特征。

修饰符


this和super


面向对象离不开this和super,这里我们分析下这两个关键字

final修饰符


final用来修饰类、变量、方法表示该类、变量、方法不可改变。

聊聊Lambda表达式


Lambda表达式是Java8新增的一个重要功能,是大家期待已久的,它使得代码更为的简洁、直观,接下来让我们了解下Lambda表达式的功能。

Thread thread = new Thread((Runnable) ()->{
            System.out.println(Thread.currentThread().getName()+"-run--");
        });

这样写也是可以的

Thread thread = new Thread(()->System.out.println(Thread.currentThread().getName()+"-run--"));
@FunctionalInterface
    interface Converter{
        Integer converter(String from);
    }
Converter converter = from ->Integer.valueOf(from);

上面代码其实就是对接口Converter的一个实现,然后把实现的地址赋给了引用变量converter。上面的代码还可以简写成

Converter converter = Integer::valueOf;

调用converter.converter("5");也就是调用Integer.valueOf("5");
Lambda还有很多有意思的写法,这就需要通过实践中去探索了。

实战


没有实战的概念就是耍流氓。

public class StaticThreadDemo implements Runnable{
    public static Integer i = new Integer(0);
    @Override
    public void run() {
        while(true){
            synchronized (i) {
                if(i<100){
                    i++;
                    System.out.println("i="+i);
                }else{
                    break;
                }
            }
        }
    }
    public static void main(String[] args) {
        Thread t1 = new Thread(new StaticThreadDemo());
        Thread t2 = new Thread(new StaticThreadDemo());
        t1.start();
        t2.start();
    }
}

问题输出的结果是啥?按顺序1-100?重复输出1-100?无序的1-100?
运行的结果是:无序的,有重复,有确实的打印1-100。
就是说,这是个线程不安全的程序。那么为什么会导致这种情况呢?
分析:
synchronized 锁对象的问题。我们知道,静态变量和类信息(区分类对象)都是存放在我们的方法区中(因此静态变量属于类本身而不属于实例),我们可以认为是线程共享的,唯一的。那,我们应该要理解的是引用i对应的对象是否被偷换的问题,如果没有变化,那么,i肯定是线程安全的。我们编译下这段代码。

public class StaticThreadDemo implements Runnable {
    public static Integer i = new Integer(0);

    public StaticThreadDemo() {
    }

    public void run() {
        while(true) {
            Integer var1 = i;
            synchronized(i) {
                if(i.intValue() >= 100) {
                    return;
                }
                Integer e = i;
                i = Integer.valueOf(i.intValue() + 1);
                System.out.println("i=" + i);
            }
        }
    }

    public static void main(String[] args) {
        Thread t1 = new Thread(new StaticThreadDemo());
        Thread t2 = new Thread(new StaticThreadDemo());
        t1.start();
        t2.start();
    }
}

我们发现:Integer要获取它的数据需要通过intValue() 方法,那么intValue()方法干了件什么事呢?查看Integer对象源码

private final int value;
  public int intValue() {
        return value;
    }

我们上面说过,对象的数据使用成员变量来描述,而这个成员变量是私有的,我们只能通过它的方法来获取。
i++分解成了两句

 Integer e = i;
 i = Integer.valueOf(i.intValue() + 1);

第一句我们比较好理解,就是用一个新的对象保存旧的数据,而第二句才是重点,我们先看下Interger的静态方法valueOf

   public static Integer valueOf(int i) {
        if (i >= IntegerCache.low && i <= IntegerCache.high)
            return IntegerCache.cache[i + (-IntegerCache.low)];
        return new Integer(i);
    }

解释一下这段代码,就是当i的字段在-128和127之间的话,从IntegerCache缓存里面获取,如果在区间之外的话,重新new一个对象,当然,缓存里面其实也是new Integer(i);所以说i的对象发生了改变了,因此,synchronized锁不住对象了。
我们可以这样理解这个流程,线程t1获取锁对象,进入run方法,执行i++后,锁对象发生了改变,这个时候线程t1,t2一起争取新的锁对象,由于这一步和打印语句并行,所以存在线程安全问题。


image.png

这里提一下Integer内部类IntegerCache缓存对象问题,在Java5加入了自动装箱和自动拆箱后(实现原理就是valueOf方法),如果int值在-128和127之间,Java不会new一个对象,而是直接从缓存里面获取了,这就有了面试题Integer a =127;Integer b = 127;Integer c =128;Integer d = 128;
System.out.println(a==b); System.out.println(c==d);

尾声

通过本章节,我们说到了面向对象的基本特性与面向过程的优势所在,然后阐述了Java面向对象的特征,引出了引用数据类型。后面我们说到了一些修饰符,如访问权限修饰符,关键字等。还提到了Java8新增的Lambda表达式的应用。总之,Java面向对象博大精深,不是一篇文章就能说得清楚的,如果要深入学习,我们还需要阅读相关的书籍。在最后,我举了一个多线程安全问题的案例,详细分析了Integer对象在i++过程中的实际操作以及对象之间的变化,希望能帮到大家进一步了解面向对象思想。

上一篇下一篇

猜你喜欢

热点阅读