Day12-Java

值传递和引用传递

  • 值传递是值的拷贝, 引用传递是引用的拷贝
public static void main(String[] args) {
    String x = new String("goeasyway");
    change(x);
    System.out.println(x);
}

public static void change(String x) {
    x = "even";
}

作者:goeasyway
链接:http://www.jianshu.com/p/c0c5e0540928
來源:
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

String 类型是引用类型, new String 创建了数据在堆, 把这堆数据的首地址存在了栈
change(x) 方法传递参数时, 将首地址传进来
x = "even"; 对 String 改变, 但是无法被记录, 因为源码里 String 是 final 类型:

public final class String
    implements java.io.Serializable, Comparable, CharSequence {

而如果是 int a = 2 来进行 change, 也无法改变, 但是因为其他原因:基本类型在方法传递时是值传递, 结果不会影响到原来的值;
如果希望字符串可以改变, 那么使用StringBuilder就可以了, 因为引用类型是首地址传递, 结果会影响到原来的值.

字节Byte, 位bit

1字节(Byte) = 8位(bit)
bit是数据存储的最小单位,也叫做比特
文字: ASCII码中一个英文字母占一个字节, 一个中文占两个字节
标点: 英文标点占一个字节, 中文标点占两个字节
1KB = 1024B(Byte)
CPU的位指CPU一次能处理的最大位数, 比如32位计算机的CPU一次最多能处理32位数据

基本数据类型的取值范围

boolean 8bit
char 16bit
int 32bit
long 64bit
float 32bit
double 64bit

String

不是基本类型, 但是希望把它作为基本类型来用(基本类型传值, 对象传引用)
简单的说是希望让String能像基本类型一样传递值(不会因为引用指向了同一个内存地址而在传递的过程中改变值.)

  • 特点: String 的内容不会变.
  • 原因:
    //JDK源码中:
    public final class String
        implements java.io.Serializable, Comparable, CharSequence {
        /** The value is used for character storage. */
        private final char value[]; //String 的本质是final的数组
      }
    
  1. 单靠 final 修饰 String 只是让 String 不可继承,
  2. 而数组 value 被final修饰, 也只是防止数组的引用地址被改, 如果使用
    final int[] value = {1,2,3,};
    value[2]=100; //数组被改成{1,2,100}
    
    或者
    final int[] value = {1,2,3,};
    Array.set(value, 2,100); //数组还是被改成{1,2,100}
    
  3. 所以还有个 private 让 value[] 只允许自己修改, 并在写 String 时不暴露出操作 value[]的方法.

静态内部类

静态内部类和非静态内部类
静态内部类是个独立的类, 比如A,B两个类, B有点特殊, 虽然独立存在, 只可以被A使用. 这时候, 如果把B并入A里, 复杂度提高, 搞得A违反了单一职责, 又可能被其他类(同一个包下的C)依赖, 不符合设计的本意, 所以不如将其变成A.B, 等于加个注释, 告诉其他类别使用B了, 它只跟A玩.
非静态内部类才是真正的内部类, 持有外部类的引用.
静态内部类英文名static nested classes(静态嵌套类)

Lambda

  1. gralde中替换编译器为jack
defualtConfig{
    useJack(true)
}
  1. 引用Java8
compileOptions {
    sourceCompatibility JavaVersion.VERSION_1_8
    targetCompatibility JavaVersion.VERSION_1_8
}

范例:

btnHandler.setOnClickListener(v -> {
    Log.i("a","a");
});

泛型 参考自Link

  • :是指 “上界通配符(Upper Bounds Wildcards)”
  • :是指 “下界通配符(Lower Bounds Wildcards)”
  1. 为什么要用通配符和边界?
    使用泛型的过程中,经常出现一种很别扭的情况。我们有Fruit类,和它的派生类Apple类

    class Fruit {}
    class Apple extends Fruit {}
    

    然后有一个最简单的容器:Plate类。盘子里可以放一个泛型的“东西”。我们可以对这个东西做最简单的“放”和“取”的动作:set( )和get( )方法。

    class Plate{
        private T item;
        public Plate(T t){item=t;
        public void set(T t){item=t;
        public T get(){return item;
    }
    

    现在定义一个盘子, 逻辑上"水果盘子"可以装水果, 也可以装苹果
    Plate p = new Plate(new Apple());
    但实际上Java编译器不允许这个操作,
    error: incompatible types: Plate cannotbe converted to Plate
    编译器的逻辑:
    苹果 IS-A 水果
    装苹果的盘子 NOT-IS-A 装水果的盘子
    所以就算容器里装的东西有继承关系, 但容器之间没有继承关系, 所以我们不可以把Plate的引用传递给Plate
    而通配符就是用来让水果盘子和苹果盘子之间发生关系

  2. 上界通配符

    Plate
    

    一个能放水果以及一切水果派生类的盘子.它和Plate的区别就是
    PlatePlatePlate的基类
    直接好处是可以用苹果盘子给水果盘子赋值了
    Plate = Plate(new Apple());
    如果把Fruit和Apple的例子扩展一下,

    class Food{};
    
    class Fruit extends Food{}
    class Meat extends Food{}
    
    class Apple extends Fruit{}
    class Apple extends Fruit{}
    class Pork extends Meat{}
    class Beef extends Meat{}
    
    class RedApple extends Apple{}
    class GreenApple extends Apple{}  
    

    上界通配符的范围是


    Day12-Java_第1张图片
  3. 下界通配符

    Plate
    

    一个能放水果以及一切水果基类的盘子, PlatePlate的基类, 但不是 Plate 的基类,

    下界通配符的范围是


    Day12-Java_第2张图片
  4. 副作用
    容器的部分功能会失效
    盘子容器具有get和set的方法

    class Plate{
      private T item;
      public Plate(T t){item = t;}
      public void set(T t){item = t;}
      public T get(){return item;}
    }  
    
  • 4.1 上界不能往里存, 只能往外取
    set 方法会失效, 但 get 方法有效
    Plate p = new Plate(new Apple());
    //不能存入元素
    p.set(new Fruit()); //ERROR
    p.set(new Apple()); //ERROR
    
    //读取出来的东西只能存放在Fruit和它的基类
    Fruit fruit = p.get();
    Object object = p.get();
    Apple apple = p.get();//ERROR
    
    编译器在看到 Plate 后, 盘子没有被标上"苹果", 而是标上一个占位符 CAP#1, 来表示捕获一个 Fruit 或 Fruit 的子类, 具体是什么类, 不知道, 所以以后想往里插入Apple 或者 Meat / Fruit, 编译器都不知道能不能和 CAP#1 匹配, 所以就都不允许.
    所以 通配符 和 类型参数 的区别在于, 对于编译器来说, 所有的T, 都代表同一种类型
    public  List fill(T...t);
    
    这里T要么都是String, 要么都是Integer, 反正保持一致
    而Plate表示, 盘子里放的是什么, 我不知道
  • 4.2 下界 可以往里存, 但是取出来时只能放在 Object;
    Plate p = new Plate(new Fruit());
    
    //存入元素正常
    p.set(new Fruit());
    p.set(new Apple());
    
    //读取出来的东西只能存放在Object
    Apple apple = p.get(); //ERROR
    Fruit fruit = p.get(); //ERROR
    Object object = p.get();
    
  1. PESC原则
    Producer Extends Consumer Super
    1. 频繁往外读取内容, 适合用上界Extends
    2. 经常往里插入内容, 适合用下界Super

参考

  • Android面试一天一题(7 Day)
  • 知乎 | Java 泛型 中 super 怎么 理解?与 extends 有何不同?| 胖胖的回答

你可能感兴趣的:(Day12-Java)