Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

从零开始学习 Android (笔记) #47

Open
creeperyang opened this issue Sep 3, 2018 · 2 comments
Open

从零开始学习 Android (笔记) #47

creeperyang opened this issue Sep 3, 2018 · 2 comments

Comments

@creeperyang
Copy link
Owner

creeperyang commented Sep 3, 2018

工作需要,开始写原生安卓项目;本文记录一个前端学习安卓相关知识的历程 😄

一、前置Java知识

https://www.w3cschool.cn/java/ 可用于快速补充一些Java的基础知识,作为一个基本的工具手册。

Java 是一门强类型的面向对象的解释型语言,通过JVM可以在多平台运行。

1. 基础类型

8种内置类型,六种数字类型(四个整数型,两个浮点型),一种字符类型,还有一种布尔型:

  • byte:8位,有符号整数,-128(-2^7)-- 127(2^7-1);
  • short:16位,有符号整数,-32768(-2^15)-- 32767(2^15 - 1);
  • int:32位,有符号整数,-2,147,483,648(-2^31)-- 2,147,483,647(2^31 - 1);
  • long:64位,有符号整数,-9,223,372,036,854,775,808(-2^63)-- 9,223,372,036,854,775,807(2^63 -1);
  • float:32位、单精度、符合IEEE 754标准的浮点数,不能表示精确的值,如货币;
  • double:64位、双精度、符合IEEE 754标准的浮点数,不能表示精确的值,如货币;
  • boolean:1位,只有两个取值:true和false;
  • char:16位,表示Unicode字符,最小值是’\u0000’(即为0),最大值是’\uffff’(即为65,535)。

引用类型

对象、数组都是引用数据类型,所有引用类型的默认值都是null。

对前端而言,需要额外注意两点:

  • 变量一旦声明,则类型确定,且不能更改(不能赋值其它类型)。

  • char 和 String 的区别:char是基本类型,对应一个字符;String 是引用类型,对应0或多个字符。
    char a = 'a'; String x = "hi!";

2. 基本语法

  • 大小写敏感:Java是大小写敏感的。
  • 类名:对于所有的类来说,类名的首字母应该大写。如果类名由若干单词组成,那么每个单词的首字母应该大写,例如 MyFirstJavaClass
  • 方法名:所有的方法名都应该以小写字母开头。如果方法名含有若干单词,则后面的每个单词首字母大写。
  • 源文件名:源文件名必须和类名相同。文件名的后缀为.java,如果文件名和类名不相同则会导致编译错误。
  • 主方法入口:Java 程序由**public static void main(String args[])**方法开始执行。

总的来说,Java基本语法和一般程序语言的语法一致。相比JS,我们可能要注意 修饰符接口等概念。

循环

和JS基本一致;不过可以注意下增强的for循环:

String [] names ={"James", "Larry", "Tom", "Lacy"};
for( String name : names ) {
         System.out.print( name );
         System.out.print(",");
}

for(声明语句 : 表达式) 中表达式为数组。

分支

与JS一致。

3. 类和对象

对象是类的一个实例,有状态和行为。类可以看成是创建Java对象的模板。一个类可以包含以下类型变量:

  • 局部变量:在方法、构造方法或者语句块中定义的变量被称为局部变量。变量声明和初始化都是在方法中,方法结束后,变量就会自动销毁。
  • 成员变量:成员变量是定义在类中,方法体之外的变量。这种变量在创建对象的时候实例化。成员变量可以被类中方法、构造方法和特定类的语句块访问。
  • 类变量:类变量也声明在类中,方法体之外,但必须声明为static类型。

每个类都有构造方法。如果没有显式为类定义构造方法,Java编译器将会为该类提供一个默认构造方法。

源文件声明规则

  • 一个源文件中只能有一个public类。
  • 一个源文件可以有多个非public类。
  • 源文件的名称应该和public类的类名保持一致。
  • 如果一个类定义在某个包中,那么package语句应该在源文件的首行。
  • 如果源文件包含import语句,那么应该放在package语句和类定义之间。如果没有package语句,那么import语句应该在源文件中最前面。
  • import语句和package语句对源文件中定义的所有类都有效。在同一源文件中,不能给不同的类不同的包声明。

Java包

包主要用来对类和接口进行分类。我们用package pkgName 来声明包,用import java.io.* 来引入包。

4. 修饰符(访问控制及其它)

Java中,可以使用访问控制符来保护对类、变量、方法和构造方法的访问。Java支持4种不同的访问权限。

  • 默认的,也称为default,在同一包内可见,不使用任何修饰符。
    接口里的变量都隐式声明为public static final,而接口里的方法默认情况下访问权限为public。

  • 私有的,以private修饰符指定,在同一类内可见。
    私有访问修饰符是最严格的访问级别,所以被声明为private的方法、变量和构造方法只能被所属类访问,并且类和接口不能声明为private。

  • 公有的,以public修饰符指定,对所有类可见。
    被声明为public的类、方法、构造方法和接口能够被任何其他类访问。

  • 受保护的,以protected修饰符指定,对同一包内的类和所有子类可见。
    被声明为protected的变量、方法和构造器能被同一个包中的任何其他类访问,也能够被不同包中的子类访问。

    protected访问修饰符不能修饰类和接口,方法和成员变量能够声明为protected,但是接口的成员变量和成员方法不能声明为protected。

    子类能访问protected修饰符声明的方法和变量。

访问控制和继承

  • 父类中声明为public的方法在子类中也必须为public。

  • 父类中声明为protected的方法在子类中要么声明为protected,要么声明为public。不能声明为private。

  • 父类中默认修饰符声明的方法,能够在子类中声明为private。

  • 父类中声明为private的方法,不能够被继承。

非访问修饰符

为了实现一些其他的功能,Java也提供了许多非访问修饰符。

  • static修饰符,用来创建类方法和类变量。

  • final修饰符,用来修饰类、方法和变量,final修饰的类不能够被继承,修饰的方法不能被继承类重新定义,修饰的变量为常量,是不可修改的。

  • abstract修饰符,用来创建抽象类和抽象方法。

    • 抽象类不能用来实例化对象,声明抽象类的唯一目的是为了将来对该类进行扩充。
    • 抽象方法是一种没有任何实现的方法,该方法的的具体实现由子类提供。抽象方法不能被声明成final和static。任何继承抽象类的子类必须实现父类的所有抽象方法,除非该子类也是抽象类。
  • synchronized和volatile修饰符,主要用于线程的编程。

5. Java 的继承与接口

Java是完全面向对象的语言,继承是最重要的topic。Java只有单继承(相比多继承,减少复杂度和潜在的一些问题(比如函数重写)),但通过接口来保留多继承的一些优点。

典型的继承语法:

class Pet {
    public String name;
    private int age = 0;
    public Pet() {}
    public String getName() {
        System.out.println("yes, in Pet!");
        return name;
    }
    public int getAge() {
        return age;
    }
}

interface Animal {
    public void eat(String food);
}

class Dog extends Pet implements Animal {
    int age = 10;

    @Override
    public int getAge() {
        System.out.println("hey!" + age);
        return age;
    }

    public void eat(String food) {
        System.out.println("dog eat " + food);
    }
}

public class Test {
    public static void main(String[] args) {
        Pet a = new Pet();
        Pet b = new Pet();
        Dog d = new Dog();
        System.out.println(a.getName());
        System.out.println(d.getName());
        System.out.println("--------------");
        d.eat("gouliang");
    }
}

子类可以从父类继承所有的 protected/public 属性和方法。

我们知道,JS 中,基于原型链的继承机制,所有实例的方法调用的方法最终都指向原型(链)的某个方法,即方法在内存中只有一份,那在Java中一样吗?

答案是:是。

Java中,类在加载时,类的信息(类信息,常量,静态变量,类方法)被存储到方法区。

类信息除了有类的版本、字段、方法、接口等描述信息外,还有一项信息是常量池(Constant Pool Table),用于存放编译期生成的各种字面量、符号引用,文字字符串、final变量值、类名和方法名常量,这部分内容将在类加载后存放到方法区的运行时常量池中。它们以数组形式访问,是调用方法、与类联系及类的对象化的桥梁。

其中 方法信息,包括方法名、返回值类型、参数类型、修饰符、异常、方法的字节码

在Java中 new 一个对象时,为类的成员(包括(继承的)父类的成员)分配了内存空间,然后执行构造函数,初始化这些属性的值。

但对象并不会为方法分配内存,当调用对象的方法时,实质上是去方法区查找到对应的方法执行。

静态方法和私有方法在解析阶段确定唯一的调用版本,而其它实例方法,会去动态查找(沿继承链)。

参考:

6. Java 泛型

泛型,即“参数化类型”。一提到参数,最熟悉的就是定义方法时有形参,然后调用此方法时传递实参。那么参数化类型怎么理解呢?顾名思义,就是将类型由原来的具体的类型参数化,类似于方法中的变量参数,此时类型也定义成参数形式(可以称之为类型形参),然后在使用/调用时传入具体的类型(类型实参)。

泛型的本质是为了参数化类型(在不创建新的类型的情况下,通过泛型指定的不同类型来控制形参具体限制的类型)。也就是说在泛型使用过程中,操作的数据类型被指定为一个参数,这种参数类型可以用在类、接口和方法中,分别被称为泛型类、泛型接口、泛型方法。

泛型类

泛型类型用于类的定义中,被称为泛型类。通过泛型可以完成对一组类的操作对外开放相同的接口。最典型的就是各种容器类,如:List、Set、Map。

泛型类的定义:

class 类名称 <泛型标识:可以随便写任意标识号,标识指定的泛型的类型>{
  private 泛型标识 /*(成员变量类型)*/ var; 
  .....
  }
}

例子:

class Demo <T> {
    private T key;
    public Demo(T key) {
        this.key = key;
    }
    public T getKey() {
        return key;
    }
}
// 泛型的类型参数只能是类类型(包括自定义类),不能是简单类型
// 传入的实参类型需与泛型的类型参数类型相同,即为Integer.
Demo<Integer> demoInt = new Demo<Integer>(10);

泛型的类型参数只能代表引用型类型,不能是原始类型(像int,double,char等)

泛型接口

//定义一个泛型接口
public interface Generator<T> {
    public T next();
}

public class FruitGenerator implements Generator<String> {

    private String[] fruits = new String[]{"Apple", "Banana", "Pear"};

    @Override
    public String next() {
        Random rand = new Random();
        return fruits[rand.nextInt(3)];
    }
}

泛型方法

泛型类,是在实例化类的时候指明泛型的具体类型;泛型方法,是在调用方法的时候指明泛型的具体类型 。

  • 所有泛型方法声明都有一个类型参数声明部分(由尖括号分隔),该类型参数声明部分在方法返回类型之前(在下面例子中的)。
  • 每一个类型参数声明部分包含一个或多个类型参数,参数间用逗号隔开。一个泛型参数,也被称为一个类型变量,是用于指定一个泛型类型名称的标识符。
  • 类型参数能被用来声明返回值类型,并且能作为泛型方法得到的实际参数类型的占位符。
  • 泛型方法方法体的声明和其他方法一样。注意类型参数只能代表引用型类型,不能是原始类型(像int,double,char等)。
public class GenericMethodTest
{
   // 泛型方法 printArray                         
   public static < E > void printArray( E[] inputArray )
   {
      // 输出数组元素            
         for ( E element : inputArray ){        
            System.out.printf( "%s ", element );
         }
         System.out.println();
    }

    public static void main( String args[] )
    {
        // 创建不同类型数组: Integer, Double 和 Character
        Integer[] intArray = { 1, 2, 3, 4, 5 };
        Double[] doubleArray = { 1.1, 2.2, 3.3, 4.4 };
        Character[] charArray = { 'H', 'E', 'L', 'L', 'O' };

        System.out.println( "Array integerArray contains:" );
        printArray( intArray  ); // 传递一个整型数组

        System.out.println( "\nArray doubleArray contains:" );
        printArray( doubleArray ); // 传递一个双精度型数组

        System.out.println( "\nArray characterArray contains:" );
        printArray( charArray ); // 传递一个字符型型数组
    } 
}

有界的类型参数:

可能有时候,你会想限制那些被允许传递到一个类型参数的类型种类范围。例如,一个操作数字的方法可能只希望接受Number或者Number子类的实例。这就是有界类型参数的目的。

要声明一个有界的类型参数,首先列出类型参数的名称,后跟extends关键字,最后紧跟它的上界。

   // 比较三个值并返回最大值
   // T 必须继承自 Comparable 接口
   public static <T extends Comparable<T>> T maximum(T x, T y, T z)
   {                     
      T max = x; // 假设x是初始最大值
      if ( y.compareTo( max ) > 0 ){
         max = y; //y 更大
      }
      if ( z.compareTo( max ) > 0 ){
         max = z; // 现在 z 更大           
      }
      return max; // 返回最大对象
   }

7. Java 多线程编程

8. Java 内存模型

源于同事的一次分享,查阅资料了解了Java虚拟机和内存管理相关知识。利于深入了解Java,对比JS可能有更大收获。

Run-Time Data Areas

2018-09-05 6 58 25

  1. 堆区:存放所有类实例(对象)和数组,虚拟机启动时创建。由GC自动管理。

  2. 方法区(Method area and runtime constant pool):存放类的结构信息,虚拟机启动时创建。类似于传统语言中存放编译后代码的地方,它存放类的 (1)run-time constant pool(类似传统语言的符号表+其它),(2)成员和方法信息,(3)静态变量,(4)方法的代码等等。

    It stores per-class structures such as the run-time constant pool, field and method data, and the code for methods and constructors.

    虽然逻辑上来说,方法区也是堆的一部分,不过方法区一般不被GC管理(取决于JVM的具体实现)。

  3. JVM Stack:每个Java线程创建时都会创建一个私有的JVM Stack,存储局部变量和部分结果,参与函数调用和返回。Stack本身只负责存储(push/pop)frames,frame 对应方法,负责存数据和部分结果,执行动态链接,返回方法的值或者dispatch异常。

  4. Native Method Stacks:线程私有,但不是所有JVM都实现了。类似JVM Stack,用于执行 Native 方法服务。

    frame通常包含:

    • 局部变量表(Current Variable Table)
    • 操作数栈(Operand Stack):字节码指令从操作数栈弹出数据,执行计算,再把结果入操作数栈。
    • 动态链接(Dynamic Linking):指向运行时常量池中frame所属方法的引用。
    • 返回地址(Return Address):正常返回,PC register的值作为返回地址保存;异常返回,通过异常处理表获得返回地址。
  5. PC register:线程私有。每个JVM线程都有自己的pc register,在任意时刻,每个线程都是在执行一个方法(记作 current method)。如果这个方法不是native的,那么pc register存着当前执行的JVM指令的地址;否则pc register的值是undefined。

@Go7hic
Copy link

Go7hic commented Sep 7, 2018

厉害了老哥

@creeperyang
Copy link
Owner Author

二、Android 编程

1. IDE 和创建第一个项目

下载 AndroidStudio,创建一个项目并编译,了解基本的流程。

2. Layout 与 Res 文件夹

3. Gradle

Gradle 是一个基于 JVM 的构建工具,基于 groovy,有强大的依赖管理,支持多工程构建。

Gradle 构建基础

projects 和 tasks是 Gradle 中最重要的两个概念。

  • 任何一个 Gradle 构建都是由一个或多个 projects 组成。每个project一般是一个jar包或者一个web应用等等。
  • 每个 project 都由多个 tasks 组成。每个 task 都代表了构建执行过程中的一个原子性操作。如编译,打包,生成 javadoc,发布到某个仓库等操作。

1、创建任务

1.1、创建一个最简单的task(build.gradle文件):

task hello {
    doLast {
        println 'Hello world!'
    }
}

我们可以执行gradle -q hello查看输出:

$ gradle -q hello
Hello world!

1.2、快速定义task:

task hello << {
    println 'Hello world!'
}
// << 等同于 doLast

1.3、使用groovy

task upper << {
    String someString = 'mY_nAmE'
    println "Original: " + someString
    println "Upper case: " + someString.toUpperCase()
}
$ gradle -q upper
Original: mY_nAmE
Upper case: MY_NAME

2、任务依赖

2.1、 在两个任务之间指明依赖关系

task hello << {
    println 'Hello world!'
}
task intro(dependsOn: hello) << {
    println "I'm Gradle"
}
$ gradle -q intro
Hello world!
I'm Gradle

2.2、延迟依赖

taskX 是可以在 taskY 之前定义的。

task taskX(dependsOn: 'taskY') << {
    println 'taskX'
}
task taskY << {
    println 'taskY'
}

3、动态任务

4.times { counter ->
    task "task$counter" << {
        println "I'm task number $counter"
    }
}
// gradle -q task1

4、任务操纵

4.1、通过 API 进行任务之间的通信 - 增加依赖

4.times { counter ->
    task "task$counter" << {
        println "I'm task number $counter"
    }
}
task0.dependsOn task2, task3

4.2、通过 API 进行任务之间的通信 - 增加任务行为

task hello << {
    println 'Hello Earth'
}
hello.doFirst {
    println 'Hello Venus'
}
hello.doLast {
    println 'Hello Mars'
}
hello << {
    println 'Hello Jupiter'
}

doFirst 和 doLast 可以进行多次调用。他们分别被添加在任务的开头和结尾。当任务开始执行时这些动作会按照既定顺序进行。

5、短标记法

每个任务都是一个脚本的属性,你可以访问它。

以属性的方式访问任务:

task hello << {
    println 'Hello world!'
}
hello.doLast {
    println "Greetings from the $hello.name task."
}
// $hello.name 就是 “hello”

6、增加自定义属性

task myTask {
    ext.myProperty = "myValue"
}

task printTaskProperties << {
    println myTask.myProperty
}

7、调用 Ant 任务

Ant 任务是 Gradle 中的一等公民。Gradle 自带了一个 AntBuilder,可以通过它来调用一个 Ant 任务以及与 Ant 中的属性进行通信。

task loadfile << {
    def files = file('../antLoadfileResources').listFiles().sort()
    files.each { File file ->
        if (file.isFile()) {
            ant.loadfile(srcFile: file, property: file.name)
            println " *** $file.name ***"
            println "${ant.properties[file.name]}"
        }
    }
}

8、方法抽取

task checksum << {
    fileList('../antLoadfileResources').each {File file ->
        ant.checksum(file: file, property: "cs_$file.name")
        println "$file.name Checksum: ${ant.properties["cs_$file.name"]}"
    }
}
task loadfile << {
    fileList('../antLoadfileResources').each {File file ->
        ant.loadfile(srcFile: file, property: file.name)
        println "I'm fond of $file.name"
    }
}
File[] fileList(String dir) {
    file(dir).listFiles({file -> file.isFile() } as FileFilter).sort()
}

9、定义默认任务

defaultTasks 'clean', 'run'
task clean << {
    println 'Default Cleaning!'
}
task run << {
    println 'Default Running!'
}
task other << {
    println "I'm not a default task!"
}
$ gradle -q
Default Cleaning!
Default Running!

以上是一些gradle的基础小知识,下面以android项目为例,了解gradle实际是怎么工作的。

Building Android Apps

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants