Java基础系列之面向对象

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

面向过程和面向对象


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

  • 面向过程
    主张按功能来设计程序,特点是:自上而下,逐步求精,模块化等。结构化程序设计的最小单元是函数,每个函数都负责完成一个功能。局限性有两点:一,设计不够直观,与人类习惯思维不一致;二,适应性差,可扩展性不强。
  • 面向对象
    更优秀的程序设计思想,基本思想是使用类、对象、继承、封装、消息等基本概念进行程序设计。最小单位是类,由类可以生成系统中多个对象。

面向对象的基本特征


  • 封装
    隐藏细节,通过公共方法暴露出该对象的功能。比如说一台电脑,我们在不拆机的情况下看不到里面的主板,cpu,内存条,这些好比是私有方法,我们无法直接访问,但是我们可以访问它的键盘,开机键,显示器,这些就是公共方法。
  • 继承
    软件复用的重要手段,子类继承父类,可以直接复用父类的属性和方法。
  • 多态
    子类对象直接赋给父类变量,运行的时候表现为子类的特性。

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

Java面向对象特征


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

  • 一切皆是对象
    除了8个基本数据类型,一切皆是对象。对象实现了数据和操作的结合,是Java的核心,具备唯一性,每个对象都有一个标识来引用,如果失去这个引用,那么这个对象将会变成垃圾,然后会被虚拟机回收掉。Java中不允许直接访问对象,而是通过一个引用(也有一种称呼为句柄)来操作对象。就如同设计一台电视机,电视机上没有任何按钮,只能通过遥控来操作电视机。而这个遥控就是引用(句柄),电视机就是对象。
    如 Person p = new Person();


    image.png

    p就是一个引用变量,其实就是C语言中的指针,只是Java友好的将这个指针封装起来了,不需要繁琐的去操作它。p中存储的是Person的地址,当访问p引用变量的成员变量和方法,其实就是访问Person的成员变量和方法。

  • 类和对象
    对象也称为实例instance,对象的抽象化是类,类的具体化是对象。Java语言使用class来定义对象,通过成员变量来描述对象的数据,通过方法来描述对象的行为特征。类之间的关系一般有两种:
    1. 一般->特殊关系(is a),Java中使用extends来表示这种特殊的关系,即继承关系。
      发生在继承关系常见的一个概念是重写(Overrride),重写必须符合规则式:两同两小一大。即,方法名,形参列表相同;返回值类型要比父类的返回值类型更小或者相等;子类抛出的异常必须比父类的异常更小或者相等(不能一代不如一代);子类的访问权限必须比父类的相等或者更大。
      这里需要注意的是当父类的方法是private修饰时,子类是不能访问的。
    2. 整体->部分关系(has a),组合关系,即Java中一个类里面保存了另一个类的引用来实现这种关系。

修饰符


  • private 私有的(类访问权限)
  • default 默认(包访问权限)
  • protected 子类访问权限
  • public 公共访问权限

this和super


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

  • this
    this关键字指向调用该方法的对象,一般会出现在构造器和方法中。我们知道一种特殊的方法static修饰的,就是静态方法,调用静态方法可以使用类对象,所以this无法指向调用该方法的对象,所以静态方法里面不能使用this,同样,静态方法中不能使用非静态成员变量。
  • super
    super是用来子类调用父类的方法或者构造方法的。和this一样,super也不能应用在静态方法中
    子类调用父类构造器过程是:
    1. 子类构造器执行体的第一行使用super显式调用父类构造器,系统会根据super传入的实例列表调用父类对应的构造器。
    2. 子类构造器执行体的第一行使用this显式的调用本类的重载构造器,执行本类的另一个构造器时即会调用父类构造器。
    3. 子类构造器既没有super,也没有this,系统将会执行子类构造器之前,隐式的调用父类的无参构造器

final修饰符


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

  • final修饰变量
    final修饰变量一旦获得初始值后是不能改变的。如下图,我们编译器在编译过程中就会报错The final local variable a may already have been assigned
    image.png

    关于final修饰成员变量,必须显式的初始化。
    1.普通成员变量,必须在初始化块(代码块)、声明时或者构造器中初始化。
    1. 静态成员变量,必须在静态代码块、声明时初始化。
      其实final的不可改变也不是绝对的,这就是final修饰基本类型变量和引用类型变量的区别,修饰引用类型时,只要保证引用类型的地址不变,而引用的这个对象完全可以改变。
  • final方法
    final方法不能被重写,如果父类不想让子类继承某个方法,可以定义为final类型。
  • final类
    final类不能有子类

聊聊Lambda表达式


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

  • 组成部分
    1. 形参列表。允许省略参数类型,如果是一个参数甚至可以省略圆括号
    2. 箭头。(->)必须是英文的划线号和大于号组成
    3. 代码块。 如果代码块只有一条语句,可以省略花括号。如果只有一条返回语句,return关键字也可以省略。
      比如说,我们可以创建一个线程类
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++过程中的实际操作以及对象之间的变化,希望能帮到大家进一步了解面向对象思想。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,332评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,508评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,812评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,607评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,728评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,919评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,071评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,802评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,256评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,576评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,712评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,389评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,032评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,798评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,026评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,473评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,606评论 2 350

推荐阅读更多精彩内容

  • 一、Java 简介 Java是由Sun Microsystems公司于1995年5月推出的Java面向对象程序设计...
    子非鱼_t_阅读 4,164评论 1 44
  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,598评论 18 399
  • 整理来自互联网 1,JDK:Java Development Kit,java的开发和运行环境,java的开发工具...
    Ncompass阅读 1,537评论 0 6
  • 幸福快乐的生活 ① 清晨醒来,我看见透过窗帘的一缕阳光,心里很温暖,前几天还被窗外叽叽喳喳叫唤的小鸟们从梦中吵醒,...
    风光美景888阅读 857评论 5 5
  • 互联网发展至今,改变了世界。我认为这是继两次工业革命以来,给世界带来的第三次巨大改变。 而技术对人类的改变,往往以...
    WillYang01阅读 440评论 0 1