java 面向对象基础

内存图



成员变量和局部变量的区别


  1. 在类中的位置不同
    • 成员变量在类中, 在方法外
    • 局部变量在方法内或方法声明中(形参)

public class Phone {
  String x; //成员变量

  public void showPhone() {
    int y; //局部变量
  }
  
}
  1. 内存中位置不同
    • 成员变量在堆内存
    • 局部变量在栈内存
  2. 生命周期不同
    • 成员变量随着对象的消失而消失
    • 局部变量随着方法的消失而消失
  3. 初始化值不同
    • 成员变量有默认值
    • 局部变量没有默认值, 必须先赋值后使用


常用API


nextLine

获取键盘输入的字符串

用法
nextLine属于Scanner类, 所以在使用nextLine时需要先创建Scanner实例, 然后调用Scanner实例的nextLine方法

 Scanner sc = new Scanner(System.in);
 String s = sc.nextLine();


String构造方法

  • String(String original)
    把字符串封装成字符串对象
  • String(char[] value)
    把字符数组封装成字符串对象
  • String(char[] value, int offset, int count)
    把字符数组中从索引offset开始的count个字符封装成字符串对象


直接赋值String和new String()的区别


String成员方法

判断

  • boolean equals(Object object)
    比较字符串内容是否相同
  • boolean equalsIgnoreCase(String str)
    比较字符串内容是否相同, 忽略大小写
  • boolean startsWith(String str)
    判断字符串是否以指定str开头
  • boolean endsWith(String str)
    判断字符串是否以指定的str结尾

获取

  • int length()
    获取字符串长度
  • char charAt(int index)
    获取index位置的字符
  • int indexOf(String str)
    获取str在字符串中第一次出现的位置
  • String substring(int start, int end)
    从start位置开始截取字符串到end位置(不包括end), 没有给end值则默认截取到最后


StringBuilder类


先看下面的图



当我们对字符串拼接时, 会把拼接的字符串放在方法区常量池的一个新的地址中, 并且我们的变量指向这个新的地址, 这就意味着拼接前的字符串在拼接之后就变成了内存中的垃圾

StringBuilder是一个可变的字符串, 可以在其后随意拼接字符串, 这样我们在拼接字符串时, 就会动态的添加到原字符串后面, 这样就不会造成内存垃圾了


StringBuilder构造方法

  • StringBuilder()
    创建一个StringBuilder类
  • `StringBuilder(String str)
    将字符串str转换为StringBuiler类型


StringBulider成员方法

  • public int capacity()
    返回当前容量
  • public int length()
    返回当前长度
  • public StringBuilder append(任意类型)
    拼接并返回StringBuilder本身
  • public StringBuilder reverse()
    反转并返回StringBuilder本身
  • public String toString()
    将StringBuilder转换成一个新的String类型字符串


将数组转换成字符串

    public static void main(String[] args) {
          int[] arr = {1,2,3};
          StringBuilder sb = new StringBuilder();
          for(int i = 0; i < arr.length; i++) {
              sb.append(arr[i]);
          }
          String newString = sb.toString();
          System.out.println(newString);
    }


ArrayList


ArrayList数组列表可以动态的创建数组, 并且封装了很多好用的数组成员方法, 可以让我们很方便的操作数组

构造函数

  • ArrayList<T>()
ArrayList<String> arr = new ArrayList<String>(); //创建以字符串为元素的ArrayList实例


成员方法

  • public boolean add(T t)
    添加元素
  • public void add(int index, T t)
    在index位置添加元素


static和代码块


static关键字

static修饰的静态方法/属性属于类, 所有该类的实例都可以调用, 其随着类而加载, 优先于对象加载

下面的代码, 我们在Student类中定义了一个static属性school, 我们在Student的实例对象p1中并没有给school赋值, 但是我们却可以直接调用p1.school

public class MyPhone {
    public static void main(String[] args) {
        Student p1 = new Student();
        p1.name = "小张";
        p1.age = 20;
        System.out.println(p1.name + ' ' + p1.age + ' ' + p1.school);
    }
}


class Student {
    public static String school = "清华";
    public String name;
    public int age;
}
  1. 由于静态方法和静态属性都是随着类的加载而加载的, 所以静态方法中可以调用静态属性和静态方法
  2. 静态方法中不能调用普通成员变量和成员方法
  3. 静态方法中没有this, 因为静态方法是随类加载的, 所以此时还没有对象


static的应用举例

Math类就是static的典型应用, Math类中的所有方法都是静态方法, 所以我们直接用Math类调用方法就可以了, 不需要创建Math实例, 并且也无法创建Math实例


继承与抽象类


继承就是让其他类拥有某个类中的成员变量和方法, 这个被继承的类就叫做父类

我们用extends关键字来继承类

看下面的代码, 我们让Man继承了Person, 所以我们在创建Man的实例对象时, 我们不但有Man的成员变量agent, 同时还拥有了继承自Person类的name, age属性, 以及say方法, 这样就可以大大减少代码量

class Person {
    String name;
    int age;

    public void say() {
         System.out.println("hello");
     }
}

class Man extends Person {
    String agent;
}


java是单继承语言

  • 一个类只能有一个父类
  • 一个父类可以有多个子类
  • java可以多层继承, 也就是说一个类可以继承其父类的父类的属性和方法


java继承中成员变量的特点

  • 子类只能继承父类非私有成员变量
  • 继承也符合变量就近原则, 子类中如果有跟父类的同名变量, 则优先使用子类中的成员变量
  • 可以用super.成员变量来调用父类的同名变量


java继承中成员方法的特点

  • 方法的重写: 子类中如果有跟父类完全相同的成员方法, 则会覆盖父类的成员方法, 在子类的实例中会优先调用子类的同名方法, 如果想调用父类的同名方法,依然使用super关键字
  • 当父类的某个方法不能满足子类的要求时, 我们可以通过重写来扩展该方法, 此时只需要在子类的同名方法中用super调用父类同名方法就可以实现在子类中对该父类方法的功能扩展
  • 在子类重写的放上前加入@Override, 这样在重写时如果有错误ide会提示错误
  • 不能重写父类的私有成员方法
class Person {
    String name;
    int age;

    public void say() {
         System.out.println("hello");
     }
}

@Override
class Man extends Person {
    public void say() {
        super.say();
        System.out.println("world");
    }
}


java继承构造方法的执行顺序

  • 在有子父类继承关系的类中, 创建子类对象时,调用子类的构造方法, 如果子类构造方法的第一行代码没有调用父类的构造函数, 则会默认的调用父类的无参构造
  • 如果想手动调用父类的构造方法, 直接在子类构造方法中的第一行使用super(参数)
class Person {
    String name;
    int age;
    
    public Person() {
        
    }

    public void say() {
         System.out.println("hello");
     }
}

class Man extends Person {
    public Man() {
        super();
    }
    public void say() {
        super.say();
        System.out.println("world");
    }
}
  • 可以在子类的构造方法中的第一行使用了this(参数)调用子类的其他构造方法, 调用顺序是:
    1. 先进入this调用的子类构造函数
    2. 判断第一行是手动调用还是默认调用父类的构造函数, 执行父类构造函数
    3. 执行this调用的子类构造函数
    4. 执行调用this的子构造函数

其实这种调用顺序是有原因的, 因为子类继承父类, 所以子类很可能要调用父类中的成员变量或者成员方法, 所以在子类调用之前预先调用父类就是为了如果子类需要用的父类的某些成员变量或者方法前, 先将其初始化


抽象类和抽象方法


abstract修饰的类和成员方法就是抽象类和抽象方法

abstract class Teacher { // 抽象类
    public abstract void say(); // 抽象方法
}
  • 抽象方法只能存在于抽象类中
  • 抽象类中可以有非抽象方法
  • 抽象类不能实例化
  • 继承抽象类的普通类必须重写抽象类中的所有抽象方法
  • 抽象类中可以有成员变量和常量


抽象类举例

下面我们抽象一个老师的类

public class MyTeacher {
    public static void main(String[] args) {
        BasicTeacher bt = new BasicTeacher();
        bt.name = "Adam";
        bt.age = 20;
        bt.gender = "male";
        bt.teach();
    }
}

abstract class Teacher {
    String name; // 姓名
    int age; // 年龄
    String gender; // 性别

    public abstract void teach();
}

class BasicTeacher extends Teacher {
    @Override
    public void teach() {
        System.out.println("基础班课程");
    }
}


接口


接口的出现主要是为了结局单一继承的局限性
接口比抽象类更抽象, 它只能有抽象方法
接口可以通过类来实现

nterface Animal {
    public abstract void eat();

}

class Cat implements Animal {
    public void eat() {
       System.out.println("吃鱼");
    }
}

接口的特点

  • 接口中所有的方法都是抽象方法无论你是否写abstract关键字
  • 实现接口必须要将其所有抽象方法都具现
  • 接口内只能定义公共静态常量
  • 一个类可以实现多个接口
  • java 9+可以在接口中使用静态方法、默认方法、私有方法、私有静态方法、常量
  • 默认方法、静态方法、私有方法、私有静态方法都必须在接口中实现


多态


多态的前提条件

  • 子父类继承关系
  • 方法的重写
  • 父类引用指向子类对象

看上面的条件可能看不懂, 实际上总的来说就是, 一个子类重写了父类的方法, 然后我们定义了一个父类类型的变量, 然后把这个变量用子类的实例赋值, 看下面的例子

public class MyAnimal {
    public static void main(String[] args) {
        Animal a = new Cat(); // 多态
        a.eat();
    }
}

class Animal {
    public void eat() {

    }

}

class Cat extends Animal {
    public void eat() {
       System.out.println("吃鱼");
    }
}
  1. 我们的Cat类继承了Animal, 并且在Cat类中重写了Animal的eat成员方法
  2. 我们在main方法中声明了一个Animal类型的变量a, 并且将它指向一个Cat实例
  3. 我们调用a的eat方法, 此时调用的是Cat的eat方法

这就是典型的多态例子


多态的成员变量及方法

  • 由于成员变量没有重写和动态绑定的概念, 所以多态中调用的成员变量为定义类的成员变量, 而不是实例对象的成员变量, 比如上面的例子, 如果我们需要调用成员变量, 那么这个变量来自于Animal而不是Cat
  • 对于重写过的普通成员方法, 多态调用的是重写后的成员方法, 比如上面的例子普通成员方法则会调用Cat实例的成员方法, 而对于重写过的静态方法则会调用Animal的静态方法, 原因是因为静态方法相当于直接调用类中的方法而非实例中的方法



多态的优缺点
缺点

  • 无法直接访问子类特有的成员变量

优点

  • 提高代码的扩展性和维护性


内部类


分类

  • 成员内部类
  • 局部内部类
  • 匿名内部类


成员内部类

所在位置和成员变量和成员方法一样, 同样可以使用修饰符修饰。下面的例子我们在Outer类中间创建了一个内部类:

class Outer {
  private int num = 10;
  
  public void show() {
    Inner in = new Inner(); //创建内部类实例对象
    in.methods(); //调用实例的methods方法, 此时num调用的是Outer的num
    System.out. println("Hello");
  }
  
  // 内部类
  public class Inner {    
    public void methods() {
      System.out.println(num);
    }
  }
}
  • 成员内部类可以直接访问外部类的成员包括私有成员, 因为我们可以把内部类看成外部类的一个特殊成员变量
  • 在相同包的其他类中若想创建该外部类的内部类实例, 可以import内部类或者按如下方法创建实例
Outer.Inner in = new Outer().new Inner();
  • 我们也可以用static修饰符修饰内部类, 修饰后内部类的性质跟静态成员变量/方法无异


局部内部类

局部内部类是定义在成员方法中的内部类

class Outer {
    private int num = 10;
    public void show() {
         // 局部内部类
        class Inner {
            private int num2 = 10;

            public void methods() {
            }
        }
        Inner in = new Inner();
        in.methods();
    }
}
  • 局部内部类的作用域跟局部变量是一样的, 只能是方法内, 所以不能在方法外创建和使用局部内部类的实例对象
  • 局部内部类应用场景有限, 一般用的不太多


匿名内部类

定义在方法内部的没有类名的内部类, 匿名内部类实际上是对某个类的继承或对某些接口的实现, 所以在使用匿名内部类时, 必须预先定义其父类或接口

class Outer {
  private int num = 10;
  
  public void show() {
     // 匿名内部类
    new Inner() {
      public void function() {}
    };
  }
}

//匿名内部类实现的接口
interface Inner {
  public void function();
}

我们还可以利用多态把匿名内部类赋值给类型为匿名内部类的父类的变量来实现匿名内部类, 如:

Inner i = new Inner(){
    public void function() {}
};


泛型


泛型是Java中一个非常重要的知识点,在Java集合类框架中泛型被广泛应用。


泛型类

假设我们创建以下Box类:

class Box{
  private String object;
  public void set(String str) {
    this.object = str;
  }
  public String get(){
    return object;
  }
}

Box有一个非常明显的局限, 只能传String类型的成员变量, 当我们要传int类型的成员变量时, 就必须要重写Box, 这要那个Box的复用性就很差了

class Box<T>{
  private T object;
  public void set(T t) {
    this.object = t;
  }
  public T get(){
    return object;
  }
}

这样我们的Box类便可以得到复用,我们可以将T替换成任何我们想要的类型:

Box<Integer> integerBox = new Box<Integer>();
Box<Double> doubleBox = new Box<Double>();
Box<String> stringBox = new Box<String>();


泛型方法

明一个泛型方法很简单,只要在返回类型前面加上一个类似<K, V>的形式就行了:

public class Util {
    public static <K, V> boolean compare(Pair<K, V> p1, Pair<K, V> p2) {
        return p1.getKey().equals(p2.getKey()) &&
               p1.getValue().equals(p2.getValue());
    }
}
public class Pair<K, V> {
    private K key;
    private V value;
    public Pair(K key, V value) {
        this.key = key;
        this.value = value;
    }
    public void setKey(K key) { this.key = key; }
    public void setValue(V value) { this.value = value; }
    public K getKey()   { return key; }
    public V getValue() { return value; }
}

我们可以像这样调用泛型方法

Pair<Integer, String> p1 = new Pair<>(1, "apple");
Pair<Integer, String> p2 = new Pair<>(2, "pear");
boolean same = Util.<Integer, String>compare(p1, p2);


多线程


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

推荐阅读更多精彩内容

  • 还不太会用简书……这玩意儿到底该怎么用的= = 又是平淡寡味的一天啊。早上睡到了9点过醒,醒过来之后还是想睡。难道...
  • 胡卜胡卜阅读 365评论 1 11
  • 踩踏事件还在不断地升级,不断有人被脚下地“东西”绊倒。四个“死神”紧随其后,收割着落单地人,这让原本拥挤地走廊更加...
    腹黑腹肌阅读 121评论 0 0