gradle入门笔记

news/2024/5/20 1:15:46 标签: gradle, java, 搜索引擎

Groovy快速入门

Groovy介绍

Groovy是一种基于JVM(Java虚拟机)的敏捷开发语言,它结合了Python、Ruby和Smalltalk的许多强大的特性,Groovy代码能够与Java代码很好地结合,也能用于扩展现有代码。由于其运行在 JVM 上的特性,Groovy也可以使用其它非Java语言编写的库

Groovy & Java & Kotlin

  • Groovy、Java及Kotlin都是基于JVM的开发语言
  • Groovy基于Java,在语法上基本相似,但也做了很多自己的扩展。Groovy除了可以面向对象编程,还可以用作纯粹的脚本语言,这一点和Kotlin是一样的
  • Goovy 和 Kotlin都有自己支持的DSL,两者有许多共通之处

Groovy特性

  • 同时支持静态类型和动态类型
  • 支持运算符重载
  • 支持DSL语法特性
  • 本地语法列表和关联数组
  • 各种标记语言,如XML和HTML原生支持
  • 对正则表达式的本地支持
  • Groovy和Java语法非常相似,可以无缝衔接使用Java
  • 支持使用现有的Java库,并做了一定的扩展

Hello, Groovy!

  • 编写Groovy,使用Java写法基本是没有问题的。但是Groovy有自己的特性,而且又是一种领域特定语言,会发现Groovy的写法就变得非常的灵活了
  • Groovy与Java写法上的一些差异
    • 分号可选,除非会导致语法歧义时才使用分号
    • return语句可选(默认返回最后一个语句)
    • 方法,属性默认都是public的
    • Groovy不强迫捕获异常,看使用者需求
    • ?.安全导航操作符的使用(kotlin借鉴了这个)
    • 闭包的使用结合DSL语法特性等等
// 1、以java的方式写groovy
class HelloGroovy{
    static void main(String[] args) {
        for(int i = 0; i< 3 ;i ++){
            println(test())
        }
    }

    // 会自动返回最后一个语句,类型会自动判别
    def test(){
        // return "hello world"
        "hello world"
    }
}

// 2、以脚本(kotlin)的方式写groovy
3.times {
    println("hello world")
}

Groovy基础语法

数据类型

  • 基本数据类型
    • byte、short、int、long、float、double、char、boolean
  • 包装类 (装箱拆箱)
    • String、Byte、Short、Integer、Long、Float、Double、Char、Boolean
  • 自动装箱
    • 因为Groovy具有动态类型特性,所以它从一开始就支持自动装箱。实际上,必要时Groovy会自动将基本类型视作对象

定义变量-动态类型

  • Java是一门静态类型的语言,但是也有自己的多态
  • 动态类型是一种更高级的多态
  • 动态类型放低了对类型的要求,使语言能够根据上下文来判定变量类型
  • 使用def关键字定义变量,不过已使用了final (不可变), priviate这样修饰符,def可以省略
class Two {
    // 自动变量
    def mile
    // 只读变量
    final year = 2027
    // def 可以省略
    private month = 12
}

def two = new Two(mile: 1000)
// 因为 year只读,下面这种方式报错
// def twotwo = new Two(mile: 1000, year: 1111)

// 获取自动装箱的类型
println two.mile.class
println two.mile.getClass()

结果
class java.lang.Integer
class java.lang.Integer

字符串

  • 单引号字符串是java.lang.String类型,同时不支持插值
  • 双引号字符串在没有使用插值表达式情况下是java.lang.String类型, 但如果有插值表达式使用的话,就是groovy.lang.GString类型
  • 三引号字符串表示多行的字符串。不必将字符串分割成几块,也不必用连接符或换行符转义字符来将字符串跨行
  • 字符串的使用
    • 单引号单个字符要表示char类型,需要使用as转换
    • ${…} 表达式进行插值,去掉花括号不引起歧义的话,可以去掉
    • 可以通过+=, -=操作符添加/减少字符 (会自动匹配)
def name = "carName"
def realName = "$name:AAAA"
println realName
println name.getClass()
println realName.getClass()

结果
carName:AAAA
class java.lang.String
class org.codehaus.groovy.runtime.GStringImpl
// 关于自动装箱

def method(Object obj){
    1
}

def method(String str){
    2
}

Object o = new Object()
println "method${method(o)} is invoked"

Object obj = "A" // 推断是String类型
println "method${method(obj)} is invoked"

结果
method1 is invoked
method2 is invoked

数组和列表

  • 数组和列表都是使用逗号分割列表的值,使用方括号括起来表示
  • Groovy中的数组和列可以随意转换
  • def定义的变量会自动推断 [ ] 类型是列表
  • Groovy列表是普通的JDK java.util.List,因为Groovy中没有定义自己的集合类
// 看起来是数组,实际是ArrayList类型
def array = [1, 2, 3]
println array.getClass()

// 下面这个才是数组类型
int[] array2 = [1, 2, 3]
println array2.getClass()

// 数组的另一个写法
def array3 = [1, 2, 3] as int[]
println array3.getClass()

结果
class java.util.ArrayList
class [I
class [I

数组的自动装箱

String[] strArray = ["1", 2, "3"]
println strArray.getClass()
println strArray[1].getClass()

结果
class [Ljava.lang.String;
class java.lang.String

范围

范围是一种特殊的列表,由序列中的第一个和最后一个值表示,Range可以是包含或排除。包含范围包括从第一个到最后一个的所有值,而独占范围包括除最后一个之外的所有值。也可以使用表达式来表示范围

  • 1…10 包含范围的示例
  • 1…<10 独占范围的示例 (开区间)
  • ‘a’…‘x’ 范围也可以由字符组成
  • 10…1 范围也可以按降序排列
  • ‘x’…‘a’ 范围也可以由字符组成并按降序排列。同步请求/异步请求实现
(1..3).forEach{
    println "hello world : " + it
    if(it == 2){
        println("2222")
    }
}

结果
hello world : 1
hello world : 2
2222
hello world : 3
class groovy.lang.IntRange

映射

  • 映射(也称为关联数组,字典,表和散列)是对象引用的无序集合
  • Map集合中的元素由键值访问
  • Map中使用的键可以是任何类,如果不能推断具体key类型,默认就是字符串

在Groovy中可以使用特定的表述方式来指定映射

  • [k1:v1,k2:v2] 具有键值对的集合
  • [:] 空映射
def map = [a: 11, b: 12]
map.forEach{k,v ->
    println "keyType=${k.class} key=$k, value=$v"
}

println "map['a'] = " + map['a']

结果
keyType=class java.lang.String key=a, value=11
keyType=class java.lang.String key=b, value=12
map['a'] = 11

运算符及控制语句

  • Groovy支持运算符重载
  • 循环语句
    • 除了和Java保持差不多的用法外,还支持结合范围的用来进行循环
    • 组合闭包来实现更简化的循环操作
  • 条件语句
    • 除了和Java保持差不多的用法外,还多了Groovy的一些扩展
    • 可以组合闭包实现更灵活的条件语句

Groovy稍微高级语法

Gradle中用到的Groovy最主要的语法

  • 闭包
  • 元编程(MOP编程)
Integer.metaClass.add1000{
    delegate+1000
}

println 250.add1000()

结果
1250

闭包,gradle中有很多这种写法

class Car{
    def miles
    private year = 2027

    def closureTest(def param1,  Closure closure){

    }
}

new Car().closureTest(1){
    
}

类与方法 - getter/setter

  • 默认会生成getter, setter方法
  • 并且可以直接像使用成员变量的方法来自动判断调用getter/setter
  • 当进行赋值时调用setter方法
  • 当直接访问值时调用的是getter方法
  • 使用’.@'才是真正的直接访问变量,跳过默认的getter/setter方法调用

groovy默认会为类中的变量生产getter/setter方法

在这里插入图片描述

car.miles = 2000
println car.miles

对变量的赋值和获取,默认是调用的getter/setter方法

Groovy对于private修饰的变量没有强制性

在这里插入图片描述

可以重写getter/setter方法(也可以看到privacy修饰符不起作用)

class Car{
    def miles
    private year = 2027

    def getMiles() {
        println "in getMiles"
        return miles
    }

    void setMiles(miles) {
        println "in setMiles"
        this.miles = miles
    }

    def getYear() {
        println "in getYear"
        return year
    }

    void setYear(year) {
        println "in setYear"
        this.year = year
    }
}

def car = new Car()
car.miles = 2000
println car.miles
car.year = 2028
println car.year

结果
in setMiles
in getMiles
2000
in setYear
in getYear
2028

Groovy中private不被限制

  • 所有的变量默认是public的
  • 如果要设置为私有禁止直接访问,仅申明private是不行的。依然可以使用’.'直接访问
  • 即使把这段代码放入到另外一个package下面也不行
  • 重载这个变量的getter/setter方法,并且在调用方法时抛出异常
  • Java中如果没有显式的指定访问修饰符(public、protected、private)那么默认是包访问权限,Groovy使用@PackageScope

如何让privacy修饰符起作用,通过抛出异常(groovy的语言不是为了安全,是为了便捷,解决这种没多大意义)

class Car{
    def miles
    private year = 2027

    def getMiles() {
        println "in getMiles"
        return miles
    }

    void setMiles(miles) {
        println "in setMiles"
        this.miles = miles
    }

    def getYear() {
        throw new IllegalAccessException("can not get")
        println "in getYear"
        return year
    }

    void setYear(year) {
        throw new IllegalAccessException("can not set")
        println "in setYear"
        this.year = year
    }
}

def car = new Car()
car.miles = 2000
println car.miles
car.year = 2028
println car.year

不通过getter/setter,直接操作变量

car.@mails = 2000

多种访问get/set方式

Car car = new Car()
car.'miles' = 10000

car['miles'] = 30000
println car.'miles'

def str = 'miles'
car."$str" = 20000

类-创建对象-使用具名参数的坑

构造方法

  • 构造方法重载规则跟Java一样
  • 但是要注意,如果没有指定具体参数的类型时,默认推断类型是Object
  • 在构造方法中传入具名参数,但是要注意:传入的参数都是键值对,实则就是一个Map类型
  • 这种方式传入的参数会自动拆解Map并且调用setter方法对应的进行赋值
  • 如果参数中还有非键值对的传参,就会把这些键值对当成Map了不会再进行自动拆解赋值。所以要有对应的构造方法才行

正常创建一个对象

class Car{
    def miles
    private year = 2027

    def getMiles() {
        println "in getMiles"
        return miles
    }

    void setMiles(miles) {
        println "in setMiles"
        this.miles = miles
    }

    def getYear() {
        println "in getYear"
        return year
    }

    void setYear(year) {
        println "in setYear"
        this.year = year
    }
}

def car = new Car(miles: 1000, year: 2028)

结果
in setMiles
in setYear

添加一个构造函数,只接收miles

class Car{
    def miles
    private year = 2027

    def getMiles() {
        println "in getMiles"
        return miles
    }

    void setMiles(miles) {
        println "in setMiles"
        this.miles = miles
    }

    def getYear() {
        println "in getYear"
        return year
    }

    void setYear(year) {
        println "in setYear"
        this.year = year
    }

    Car(miles){
        this.miles = miles
    }
}

def car = new Car(miles: 1000, year: 2028)
println car.miles
println car.miles.getClass()

结果
in getMiles
[miles:1000, year:2028]
in getMiles
class java.util.LinkedHashMap

1、可以看到,创建对象时,new Car中传递参数是当做map进行传递的

2、由于 下面构造方式没有指明参数类型,就把miles当做Object对象,接受了传递进来的map对象

Car(miles){
    this.miles = miles
}

如果想让只具名参数和只接收一个参数的构造方法生效

  • 需要创建一个空的构造方法
  • 一个参数的构造方法,必须指定类型,否则还是识别为接收map的object对象
class Car{
    def miles
    private year = 2027

    def getMiles() {
        println "in getMiles"
        return miles
    }

    void setMiles(miles) {
        println "in setMiles"
        this.miles = miles
    }

    def getYear() {
        println "in getYear"
        return year
    }

    void setYear(year) {
        println "in setYear"
        this.year = year
    }

    Car(){

    }
    Car(int miles){
        this.miles = miles
    }
}

def car = new Car(miles: 1000, year: 2028)
println car.miles
println car.miles.getClass()

def car2 = new Car(miles: 2000)
println car2.miles
println car2.miles.getClass()

结果
in setMiles
in setYear
in getMiles
1000
in getMiles
class java.lang.Integer
in setMiles
in getMiles
2000
in getMiles
class java.lang.Integer

普通类中方法不支持具名参数 - 神经病

class Car {
    def miles
    def year


    void execute(x = 1, y = 2, z=3) {
        println "$x $y $z"
    }

}
Car c = new Car()
c.execute(x: 1, y: 2, z: 3) // 不支持具名参数,会把传递进来的方法当做map
c.execute(x: 1, y: 2, z: 3, 2222, 3333) // 这个是可以的,正常的(传递了3个参数,一个是map,和两个整数)
c.execute(x: 1, y: 2, z: 3, 2222, c: 111, 3333, a: 111, b: 222) // 这个也是可以的,多有的键值对合并,成为第一个参数

// 下面的结果,xyz结果都是1,2,3 没有用到变量进行赋值
c.execute(x = 1, y = 2, z = 3)
c.execute(x = 1, z = 2, y = 3)

默认参数

class Car {
    def miles
    def year


    void execute(x = 1, y = 2, z=3) {
        println "$x $y $z"
    }
  
  // 这个会报错,这和kotlin一样
  void execute() {
        println "$x $y $z"
    }

}

Groovy闭包

Groovy中可以理解为闭包就是可执行的代码块,或匿名函数。闭包在使用上与函数与许多共通之处,但是闭包可以作为一个函数的参数

  • 默认情况下,闭包能接收一个参数,且参数字段默认使用it
  • 在箭头前面没有定义参数,这时候闭包不能传入参数
  • 可以定义多个接收的参数
  • 使用参数默认值,跟方法使用规则一样!

一个最简单的闭包

def closure = {
    // 这个里面和方法体一样
  	// 默认接收一个参数,参数直接使用it
}

// 有一个默认参数的闭包
def closure = {
    println it
}
closure("show this")

// 没有默认参数的闭包
def closure = {
    -> 
    println "not arguments"
}

closure()


// 定义多参数的闭包,支持默认值
def closure = {
    arg1="ccc", arg2 ->
    println "$arg1 and $arg2"
}

closure("aaa", "bbb")

闭包就是Closure对象

def abc = {

}

println abc.class

结果 - 动态运行时生成的
class com.study.class1.TwoThis$_run_closure2 

闭包可以作为方法的参数

def func(closure) {
   closure()
}

func {
   // 闭包执行的代码
   println "call"
}

柯里化闭包

  • 使用curry方法给闭包参数加默认值,生成的新的闭包,就等于设置了参数默认值的闭包
  • def curriedClosure = closure2.curry(“20”) // curry从左到右设置参数默认值

闭包与接口/类进行转换

  • 即使定义的方法传入的是闭包,但是如果传入的对象的类型也有call方法,那么,是可以执行这个对象的call方法的,实际上,闭包执行的也是call方法
  • 在一个对象上调用(),表示调用这个对象的call方法
    • def action = new Action()
    • action()
interface Action {
   void aaa()
}
def closure = {
   println "aaa"
}
Action action = closure
action.aaa()

println closure.class
println action.class // 动态代理对象
interface Action {
   void aaa()
   void bbb()
   void ccc()
}

def addTextWatcher(Action action) {
   action.aaa()
}
addTextWatcher({
   println "aaa"
} as Action)
// 当有接口有多个方法时,传入的闭包要强转为Action,使用as关键字
// 现在看起来就像是gradle中的内容了

闭包中的内容是通过Closure中的call方法进行调用

interface Action {
   void call()
}

def func(closure) {
   closure()
}

Action closure = new Action() {
   @Override
   void call() {
       println "call"
   }
} as Closure

func(closure)

类当中如果定义了call方法,可以直接使用对象()去调用call方法

class Action {
   void call(a) {
       println "$a"
   }
}

def a = new Action()
a(111)

Closure比较重要的成员变量

private Object delegate;
private Object owner; 
private Object thisObject;

// delegate、owner、thisObject 
// 在普通闭包中,都是定义这个普通闭包的对象
// 在静态闭包中,都是定义这个静态闭包的类

private int resolveStrategy = OWNER_FIRST;
protected Class[] parameterTypes; // 参数类型
protected int maximumNumberOfParameters; // 参数个数
def closure = {
   int a, int b ->
       println a.getClass()
       println b.getClass()
}
closure(1, 2)
println closure.parameterTypes
println closure.maximumNumberOfParameters

def closure2 = {
   println "this is" + this
   println "owner is" + owner
   println "delegate is" + delegate
}
closure2()
class Test {
   def closure2 = {
       println "this is" + this
       println "owner is" + owner
       println "delegate is" + delegate
   }
}

class Test {
   static def closure2 = {
       println "this is" + this
       println "owner is" + owner
       println "delegate is" + delegate
   }
}


class Test {
  
   def closure1 = {
       def closure2 = {
           // this:Test@2b76ff4e,定义它的时候的类的this,当它是static时就是class对象
           println "this is" + this
           // owner:Test$_closure1@8c3619e,定义它的时候的类的对象
           println "owner is" + owner
           // delegate:Test$_closure1@8c3619e,默认就是owner, 但是代理可以修改
           println "delegate is" + delegate
       }
       closure2()
   }
}

闭包中代理策略

  • delegate默认就是owner,在执行脚本中可以直接调用方法
  • 但是如果方法放入类中就不可以了,可以通过修改代理的方式,让它能够调用
  • 代理策略,默认的是选择owner
  • 闭包有以下代理策略
    • groovy.lang.Closure#DELEGATE_FIRST // delegate优先
    • groovy.lang.Closure#DELEGATE_ONLY // 只在delegate中找
    • groovy.lang.Closure#OWNER_FIRST // owner优先
    • groovy.lang.Closure#OWNER_ONLY // 只在owner找
    • groovy.lang.Closure#TO_SELF // 只在自身找(闭包内部),意义不大

在脚本中,func不在类中,下面的脚本可以直接访问

def func(){
	println "func"
}

def closure = {
	func()
}

closure()

当func在类中时,下面闭包访问就报错


class Test2 {
   def func() {
       println "Test2 func"
   }
}

def closure = {
   func()
}
closure()

修改代理,就能访问了


class Test2 {
   def func() {
       println "Test2 func"
   }
}

def closure = {
   func()
}

closure.delegate = new Test2()
closure()

但是下面的代码结果是Script func

class Test2 {
   def func() {
       println "Test2 func"
   }
}

def func() {
   println "Script func"
}

def closure = {
   func()
}

closure.delegate = new Test2()
closure()

修改代理,修改策略

class Test2 {
   def func() {
       println "Test2 func"
   }
}

def func() {
   println "Script func"
}
def closure = {
   func()
}
closure()

closure.delegate = new Test2()// 优先选择的是owner
//* @see groovy.lang.Closure#DELEGATE_FIRST
//* @see groovy.lang.Closure#DELEGATE_ONLY
//* @see groovy.lang.Closure#OWNER_FIRST
//* @see groovy.lang.Closure#OWNER_ONLY
//* @see groovy.lang.Closure#TO_SELF
closure.resolveStrategy=Closure.DELEGATE_ONLY
closure()

动态/静态类型语言

  • 动态类型语言:动态类型语言是指在运行期间才去做数据类型检查的语言,也就是说,在用动态类型的语言编程时,可以不用给变量指定数据类型,该语言会在你第一次赋值给变量时,在内部将数据类型记录下来。Python和Ruby这些就是一种典型的动态类型语言
  • 静态类型语言:静态类型语言与动态类型语言刚好相反,它的数据类型是在编译其间检查的,也就是说在写程序时要声明所有变量的数据类型,C/C++是静态类型语言的典型代表,其他的静态类型语言还有C#、Java等

groovy的动态特性

class User {
    def username = 'zee'
    String age

    void setName(String name) {
        println "setName(String name)"
        this.username = name
    }

    void setName(Object name) {
        println "setName(Object name)"
        this.username = name
    }
}

def user = new User()
println user.username.class

user.username = new Object()
println user.username.class

user.username = 123
println user.username.class

user.username = new User()
println user.username.class




def user1 = new User()
Object name = "zee"
println name.class

user1.setName(name)
name = 123
println name.class
user1.setName(name)

def user3 = new User()
user3.age = "123"
user3.age = 123 // groovy是强类型的,动态的,这里int可以转成 String
println user3.age

结果
class java.lang.String
class java.lang.Object
class java.lang.Integer
class com.study.class1.User
class java.lang.String
setName(String name)
class java.lang.Integer
setName(Object name)
123

groovy不会进行安全检查,在运行时候才抛出异常

class Person1 {
    def dream() {
        println "I have a dream!"
    }
}

class Person2 {
    def dream() {
        println "I have a dream!"
    }
}

def func(person){
    person.dream()
}

def person1 = new Person1()
def person2 = new Person2()
func(person1)
func(person2)

使用@TypeChecked强制编译检查,但是会损失动态特性

class Person1 {
    def dream() {
        println "I have a dream!"
    }
}

class Person2 {
    def dream() {
        println "I have a dream!"
    }
}

@TypeChecked
def func(Person1 person) { // 这里必须指定类型
    person.dream()
}

def person1 = new Person1()
def person2 = new Person2()

func(person1)
func(person2)


@TypeChecked
class Test {

    @TypeChecked(TypeCheckingMode.SKIP) // 跳过检查
    def func(person) {
        person.dream()
    }
}

动态特性及元编程

动态特性

  • 根据上下文推断具体类型
    • def file = new File(“”);
    • file.getParent()
  • CallSite动态调用节点
  • java中要使用继承才能实现多态,而Groovy轻而易举
    • 优势:灵活
    • 缺点:编译时不会检查类型,运行时报错

元编程

  • Java中可以通过反射,可以在运行时动态的获取类的属性,方法等信息,然后反射调用。但是没法直接做到往内中注入变量、方法;不过Java也有动态字节码技术:ASM,JVM TI,javassist等等
  • MOP(元对象协议):Meta Object Protocol
  • Groovy直接可以使用MOP进行元编程,我们可以基于应用当前的状态,动态的添加或者改变类的方法和行为。比如在某个Groovy类中并没有实现某个方法,这个方法的具体操作由服务器来控制,使用元编程,为这个类动态添加方法,或者替换原来的实现,然后可以进行调用

MOP方法拦截

  • 实现GroovyInterceptable接口,重写invokeMethod来进行拦截
  • 使用MetaClass拦截方法,覆盖invokeMethod方法
    • 使用类的MetaClass,针对的是class对象,所有实例都会被影响
    • 使用具体实例的MetaClass,只影响当前对象实例
class Person implements GroovyInterceptable {

    def func() {
        System.out.println "I have a dream!"
    }

    @Override
    Object invokeMethod(String name, Object args) {
        //println "invokeMethod"
        System.out.println "$name invokeMethod"
        //respondsTo(name) //判断方法是否存在
        if (metaClass.invokeMethod(this, 'respondsTo', name, args)) {
            System.out.println "$name 方法存在"
            System.out.println "$name 执行前.."
            metaClass.invokeMethod(this, name, args)
            System.out.println "$name 执行后.."
        }
    }
}

new Person().func()


结果
func invokeMethod
func 方法存在
func 执行前..
I have a dream!
func 执行后..

这里拦截某个对象的某一个方法

class Person3 {

    def func() {
        System.out.println "I have a dream!"
    }
}

def person3 = new Person3()
// 这里拦截某个对象的某一个方法
person3.metaClass.func = {
   println "I have a new dream !!!"
}
person3.func()

// 拦截所有的方法
person3.metaClass.invokeMethod = {
    String name, Object args ->// invokeMethod(String name, Object args)
        println "$name 被拦截"
}
person3.func()

拦截所有实例的方法

Person3.metaClass.invokeMethod = {
    String name, Object args ->// invokeMethod(String name, Object args)
        println "$name 被拦截"
}

对内置String进行拦截

String.metaClass.invokeMethod = {
    String name, Object args ->
        println "String.metaClass.invokeMethod"
        MetaMethod method = delegate.metaClass.getMetaMethod(name)
        if (method != null && name == 'toString') {
            "showme"
        }
}
println "jason".toString()

MOP方法注入

方法注入:编写代码时知道想要添加到一个或多个类中的方法的名字。利用方法注入,可以动态地向类中添加行为。也可以向任意数目的类中注入一组实现某一特定功能的可服用方法,就像工具函数。有以下几种方式

  • 使用分类注入方法
  • 使用ExpandoMetaClass注入方法
    • 直接使用类或实例的MetaClass注入方法,实际上最终操作的类型是ExpandoMetaClass。手动创建ExpandoMetaClass来进行注入
  • 使用Mixin注入方法。( 在类中可以使用多个Mixin )

使用MetaClass来注入方法

class Person4 {
    def func() {
        System.out.println "I have a dream!"
    }
}

println Person4.metaClass // 注入前:org.codehaus.groovy.runtime.HandleMetaClass

Person4.metaClass.newFunc = {
   println "newFunc调用"
}
println Person4.metaClass // 注入后:groovy.lang.ExpandoMetaClass

使用groovy.lang.ExpandoMetaClass来注入方法

def emc = new ExpandoMetaClass(Person4)
emc.func2 = {
   println "func2"
}
emc.initialize()
Person4.metaClass = emc
new Person4().func2()

使用分类注入方法 - 场景,框架工具类

class StringUtils {

//    public static String isEmpty(){
//
//    }

   static def isNotEmpty(String self) {
       println "isNotEmpty"
       self != null && self.length() > 0
   }

}

class StringUtils2 {

   static def isNotEmpty(Integer self) {
       println "isNotEmpty2"
       //self != null && self.length() > 0
   }
}

use(StringUtils, StringUtils2) { // 从后往前找
   println "".isNotEmpty()
   println 1.isNotEmpty()
}

其他写法

@Category(String)
class StringUtils2 {
   def isNotEmpty() {
       println "isNotEmpty2"
       this != null && this.length() > 0
   }
}

use(StringUtils2) {
   println "".isNotEmpty()
}

操作不存在的变量、方法

class Person5 {
   def username

   // 对不存在的变量进行get操作
   def propertyMissing(String name) {
       println "propertyMissing"
       if (name == 'age') {
           "19" // 返回默认值
       }
   }

   // 对不存在的变量进行set操作
   def propertyMissing(String name, def arg) {
       println "propertyMissing ${name} : arg${arg}"
       return "default"// 给与返回值
   }

   def methodMissing(String name, def arg) {
       println "$name methodMissing"
       if (name.startsWith 'getFather') {
           "zzzz"
       }
   }

}

def p = new Person5()
println p.age = 12
println p.age
println p.getFather()

运算符重载

运算符重载方法列表

a + b         a.plus(b)
a – b         a.minus(b)
a * b         a.multiply(b)
a ** b        a.power(b)
a / b         a.div(b)
a % b         a.mod(b)
a | b         a.or(b)
a & b         a.and(b)
a ^ b         a.xor(b)
a++ or ++a    a.next()
a– or –a      a.previous()
a[b]          a.getAt(b)
a[b] = c      a.putAt(b, c)
a << b        a.leftShift(b)
a >> b        a.rightShift(b)
~a            a.bitwiseNegate()
-a            a.negative()
+a            a.positive()

switch(a) { case(b) : }
b.isCase(a)

a == b
a.equals(b) or a.compareTo(b) == 0

a != b
! a.equals(b)

a <=> b
a.compareTo(b)

a > b
a.compareTo(b) > 0

a >= b
a.compareTo(b) >= 0

a < b
a.compareTo(b) < 0

a <= b
a.compareTo(b) <= 0
class TestA {
   def plus(TestA newTestA) {
       // 处理业务
   }
}
new TestA() + new TestA()

Integer.metaClass.plus = {
   int a ->
       "$delegate + $a"
}
println 1 + 2

Expando动态生成类

def e = new Expando(name: "Aaa", func: {
   println "func"
})

e.a = 123
println e.a

gradle_1498">gradle参考

https://blog.csdn.net/nihaomabmt/article/details/112308851

gradle_1501">为什么要学习gradle

gradle_1502">gradle自身介绍

  • Gradle是一款基于Apache的Ant和Maven概念的项目自动化开源构建工具。Gradle的核心是基于Java来实现的,可以把Gradle看成就是一个轻量级的Java应用程序
  • Gradle使用Groovy、Kotlin等语言编写自定义脚本,取代了Ant和Maven使用xml配置文件的方式,很大程度简化了开发时对项目构建要做的配置,使用更加灵活和强大

gradle_1509">学习gradle的目的

  • Gradle是目前Andorid最主流的构建工具
  • 不少技术领域如组件化、插件化、热修复,及构建系统 (很多优秀的框架都是在编译时或者打包之前做一些特殊的处理) ,都需要通过Gradle来实现
  • 不懂Gradle将无法完成上述事情,所以学习Gradle非常必要

关于项目构建

  • 对于Java应用程序,编写好的Java代码需要编译成.class文件才能够执行。所以任何的Java应用开发,最终都需要经过这一步
  • 编译好了这些class文件,还需要对其进行打包。打包不仅针对于这些class文件,还有所有的资源文件等。比如web工程打包成jar包或者war包就包含了自己的资源文件,然后放到服务器上运行
  • Andorid工程编译好的class文件还要被打包到dex包中,并且所有的资源文件进行合并处理,甚至还需要对最终打包出来的文件进行加密和签名处理等等

Android build

在这里插入图片描述

gradle使用安卓插件编译android项目

自动化构建工具的发展

  • “石器时代”:自己编写命令脚本,进行编译和打包
  • “蒸汽时代”:Make、Ant工具的出现
  • “电气时代”:Maven
  • “信息时代”:Gradle,更高级的自动构建工具出现

gradle_1543">gradle提供了什么

  • 对多工程的构建支持非常出色,尤其是工程依赖问题,并支持局部构建
  • 多种方式的依赖管理:如远程Maven仓库,nexus私服,ivy仓库或者本地文件系统等
  • 支持传递性依赖管理
  • 轻松迁移项目工程
  • 基于Groovy等语言构建脚本,简便且灵活
  • 免费开源,并且整体设计是以作为一种语言为导向的,而非成为一个严格死板的框架

Gradle基础-Gradle构建机制-Gradle任务

win 10 下环境搭建

获取gradle

  • 从官网下载 https://gradle.org/releases/
  • 如果已经使用过android studio编译过项目,可使用本地用户文件夹下的gradle工具包 C:\Users\showme.gradle\wrapper\dists

这里使用本地缓存的gradle工具包

C:\Users\showme.gradle\wrapper\dists\gradle-7.4.2-bin\48ivgl02cpt2ed3fh9dbalvx8\gradle-7.4.2

系统属性配置

添加 GRADLE_HOME C:\Users\showme.gradle\wrapper\dists\gradle-7.4.2-bin\48ivgl02cpt2ed3fh9dbalvx8\gradle-7.4.2

添加 Path %GRADLE_HOME%\bin

通过命令行终端输入gradle --version 查看是否配置成功

PS C:\Users\showme> gradle --version

------------------------------------------------------------
Gradle 7.4.2
------------------------------------------------------------

Build time:   2022-03-31 15:25:29 UTC
Revision:     540473b8118064efcc264694cbcaa4b677f61041

Kotlin:       1.5.31
Groovy:       3.0.9
Ant:          Apache Ant(TM) version 1.10.11 compiled on July 10 2021
JVM:          17.0.2 (Oracle Corporation 17.0.2+8-LTS-86)
OS:           Windows 10 10.0 amd64

Hello Gradle

在工程文件夹下,创建一个build.gradle文件,包含内容

println 'Hello, Gradle!'

build.gradle是构建Project的核心文件,也是入口

  • 如果没有该文件,会出现not found in root project ‘xxxxx’ 提示异常
  • 必须要有一个可以运行的task,运行后自动生成.gradle文件夹下的内容

打开cmd终端,移动到工程目录下,执行命令:> gradle help

$ gradle help                                                                                                                            [0:31:04]
Starting a Gradle Daemon (subsequent builds will be faster)

> Configure project :
Hello, Gradle!

> Task :help

Welcome to Gradle 7.4.2.

To run a build, run gradle <task> ...

To see a list of available tasks, run gradle tasks

To see more detail about a task, run gradle help --task <task>

To see a list of command-line options, run gradle --help

For more detail on using Gradle, see https://docs.gradle.org/7.4.2/userguide/command_line_interface.html

For troubleshooting, visit https://help.gradle.org

从输出可以看到

  • 生成了一个Gradle Daemon
  • 执行了gradle脚本中的输出语句
  • 打印了帮助信息

在看一下,目录下当前没有生产额外的文件

showme@showmedeMBP: ~/workspace/tmmmp/hello_gradle
$ tree .                                                                                                                                 [0:34:09]
.
└── build.gradle

0 directories, 1 file

Gradle Wrapper

  • Gradle Wrapper用来配置开发过程中用到的Gradle构建工具版本。避免因为Gradle不统一带来的不必要的问题
  • 在工程目录下使用cmd命令生成wrapper:> gradle wrapper
  • 标准的gradle工程目录
    • gradlew和gradlew.bat分别是Linux和Windows下的可执行脚本
    • 具体业务逻辑是在/gradle/wrapper/gradle-wrapper.jar中实现,gradlew最终还是使用Java执行这个jar包来执行相关的Gradle操作的
showme@showmedeMBP: ~/workspace/tmmmp/hello_gradle
$ gradle wrapper                                                                                                                         [0:37:24]

> Configure project :
Hello, Gradle!

BUILD SUCCESSFUL in 696ms
1 actionable task: 1 executed

showme@showmedeMBP: ~/workspace/tmmmp/hello_gradle
$ tree .                                                                                                                                 [0:37:31]
.
├── build.gradle
├── gradle
│   └── wrapper
│       ├── gradle-wrapper.jar
│       └── gradle-wrapper.properties
├── gradlew
└── gradlew.bat

2 directories, 5 files

gradle/wrapper/gradle-wrapper.properties文件的目的:在不同的机器上快速部署gradle执行环境

distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
  • distributionBase 下载的Gradle压缩包解压后存储的主目录

  • zipStorePath | distributionPath: 本地缓存的gradle可执行文件路径, 默认在 ~/.gradle/wrapper/dists

  • distributionUrl 下载特定gradle版本

    • https://services.gradle.org/distributions/gradle-7.4.2-bin.zip

    • https://services.gradle.org/distributions/gradle-7.4.2-all.zip

    • bin和all的区别,bin只包含了可执行文件相关的内容,all还包含了gradle的源码和帮助文档

GRADLE_USER_HOME

  • 默认路径在~/.gradle/ ,不建议使用本地maven的m2替代,因为原本的.gradle目录下的模块分的很清晰,功能明确
  • 如果启动时,指定参数,使用别的目录代替GradleUserHome ,后果是每次构建需要重新下载插件与依赖到新的目录
  • 默认情况下,gradle运行时,除了和项目打交道,还有当前项目构建的全新的GradleUserHome目录,所有jar包都得重新下载

gradlew命令行

  • gradlew -?/-h/-help 使用帮助
  • gradlew tasks 查看所有可执行Tasks
  • gradlew --refresh-dependencies assemble 强制刷新依赖
  • gradlew cBC 等价与执行Task cleanBuildCache,这种通过缩写名快速执行任务
  • gradlew :app:dependencies 查找app工程依赖树

在这里插入图片描述

Gradle构建机制

gradle标准工程

showme@showmedeMBP: ~/workspace/tmmmp/hello_gradle
$ tree .                                                                                                                                 [0:37:31]
.
├── build.gradle
├── gradle
│   └── wrapper
│       ├── gradle-wrapper.jar
│       └── gradle-wrapper.properties
├── gradlew
└── gradlew.bat

java中的标准工程

在这里插入图片描述

安卓中的标准工程:有一个app模块,在这个模块中包含也包含了一个build.gradle

  • 最外部的build.gradle适用于整个工程
  • app模块中的build.gradle适用于当前模块

在这里插入图片描述

Gradle DSL

  • DSL(Domain Specific Language) 领域特定语言,或领域专属语言。简单来说就是专门关注某一领域的语言,它在于专而不是全,最典型的比如HTML
  • Gradle可以使用Groovy DSL,专门用来开发Gradle的构建脚本。所以说Gradle整体设计是以作为一种语言为导向的,并非成为一个严格死板的框架

App->build.gradle文件 , 使用的就是groovy 语言

plugins {
    id 'com.android.application'
}

android {
    compileSdk 32

    defaultConfig {
        applicationId "com.example.studygradle"
        minSdk 26
        targetSdk 32
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
}

dependencies {

    implementation 'androidx.appcompat:appcompat:1.3.0'
    implementation 'com.google.android.material:material:1.4.0'
    implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
    testImplementation 'junit:junit:4.13.2'
    androidTestImplementation 'androidx.test.ext:junit:1.1.3'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
}

gradle_1823">settings.gradle

  • Gradle支持多工程构建,使用settings.gradle来配置添加子工程(模块)
  • settings文件在初始化阶段执行,创建Settings对象,在执行脚本时调用该对象的方法
  • Settings.include(String… projectPaths)
    • 将给定的目录添加到项目构建中,‘:app’表示文件相对路径,相当于’./app’文件夹
    • 多项目架构进行分层,把同层次的子工程放在同一文件夹下便于管理,使用’:xxx:yyy’表示

在android studio中新增java模块(多层次的模块写法 - 这个冒号就相当于文件夹的斜杆

在这里插入图片描述

可以看到setting.gradle通过include命令引入的模块,已经分层方式

模块不include进来,则该模块不会进行构建

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PPnqRKCX-1657964865416)(/Users/showme/我的云端硬盘/核心/android/android编译时技术/基础03-<a class=gradle/01-gradle基础-gradle构建机制-gradle任务/01-gradle基础-gradle构建机制-gradle任务.assets/image-20220630011124298.png)]" />

可以点进去看include细节,发现是gradle构建工具的一个方法

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7EKl0pQR-1657964865417)(/Users/showme/我的云端硬盘/核心/android/android编译时技术/基础03-<a class=gradle/01-gradle基础-gradle构建机制-gradle任务/01-gradle基础-gradle构建机制-gradle任务.assets/image-20220630011551817.png)]" />

rootProject.name 配置的是整个工程最顶层的名字

在这里插入图片描述

gradle_1861">build.gradle

  • build.gradle是项目构建文件,每个工程都有一个build.gradle文件
  • build.gradle在配置阶段执行,并创建相应工程的Project对象,执行的代码可以直接调用该对象提供的方法或属性
  • 最顶层的build.gradle适用于整个项目,每个模块中的build.gradle适用于模块自身

Daemon(守护进程)

  • 项目启动时,会开启一个client,然后启动一个daemon,通过client向daemon收发请求,项目关闭,client关闭,daemon保持启动,有类似项目再次部署时,会直接通过新的client访问已经启动的daemon,所以速度很快,默认daemon不使用3小时后关闭;不同项目兼容性考虑,也可使用–no-daemon 启动项目,就没有速度优势了
  • 手动停止daemon:gradle wrapper --stop

Gradle生命周期

  • Initialization
    • Gradle支持单项目和多项目构建。在初始化阶段,Gradle确定哪些项目将参与构建,并为每个项目创建Project实例,一般我们不会接触到它。(比如解析settings.gradle)
  • Configuration
    • 配置阶段,解析每个工程的build.gradle文件,创建要执行的任务子集和确定各种任务之间的关系,并对任务的做一些初始化配置
  • Execution
    • 运行阶段,Gradle根据配置阶段创建和配置的要执行的任务子集,执行任务

在这里插入图片描述

配置阶段

  • 解析每个Project中的build.gradle,解析过程中并不会执行各个build.gradle中的task
  • 经过Configration阶段,Project之间及内部Task之间的关系就确定了。一个Project包含很多Task,每个Task之间有依赖关系。Configuration会建立一个有向图来描述Task之间的依赖关系,所有Project配置完成后,会有一个回调project.afterEvaluate(),表示所有的模块都已经配置完了

task

  • task是gardle中最小的任务单元,任务之间可以进行复杂的操作(如动态创建任务,多任务间依赖调用等等)。gradle的执行其实就是由各种任务组合执行,来对项目进行构建的
  • 使用gradlew help命令,任何gradle项目都有一个该task,可以执行此命令观察taks执行的流程是否如预期
  • 可以使用工具查看,还可以通过 gradlew tasks 命令查看可运行任务
    • 使用gradlew tasks --all 命令查看所有任务
    • 使用gradlew A B 命令表示执行任务A和B,支持驼峰简写

gradle_1901">在build.gradle中自定义任务

  • task <任务名>{ … },在Gradle5.x以上已经删除<<操作符这种写法
  • { … }执行的是配置阶段的代码,执行阶段要处理的逻辑需要调用doFirst、doLast方法,在闭包中实现。doFirst{}表示任务执行开始时调用的方法,doLast{}表示任务执行结束调用的方法
  • task A(dependsOn:[B]){ … } 表示任务A依赖于任务B,那么B执行在A之前
  • 自定义的任务默认分组到other中

自定义task类

// 通过自定义任务类来实现自定义任务
class MyTask extends DefaultTask {

    @Input
    String filePath // 可以指定filePath来传入n个文件的路径

    @OutputFile
    File file

    MyTask() {
        group "自定义任务"
        description "自定义任务描述"
    }

    @TaskAction
    void action() {
        println "MyTask action()"
    }

    @TaskAction
    void action2() {
        println "MyTask action2()"
    }

}

// 继承自定义的task类,并创建新的task
task myTask2(type: MyTask) {
    filePath = 'xxx/xxx'
    file = file('test.txt')
}


// 继承已有的task
task clean(type: Delete) {
    delete rootProject.buildDir
}

task之间的集成和依赖关系

// Gradle3.0 << ,在4.x以上就废弃了
task A {
    println "configuration A.."

    doFirst {
        println "doFirst A.."
    }
   doLast {
       println "doLast A1.."
   }
   doLast {
       println "doLast A2.."
   }
   doLast {
       println "doLast A3.."
   }
}

A.doLast {
   println "doLast A4.."
}

task C {
   println "configuration C.."

   doLast {
       println "doLast C.."
   }
}
task B {
   println "configuration B.."

   doLast {
       println "doLast B.."
   }
}

task hello2(dependsOn: [A, C, B]) {
   doLast {
       println "doLast hello2"
   }
}

A.dependsOn B
A.mustRunAfter(B)
A.shouldRunAfter B

task finalized {
   doLast {
       println "清理任务"
   }
}

A.finalizedBy finalized

依赖其他build.gradle文件
1)创建一个gradle文件
config.gradle

task CCCC {
    doLast {
        println "doLast.."
    }
}

2)依赖创建的其他gradle文件

apply from: 'config.gradle'

DefaultTask

  • task定义的任务其实就是DefaultTask的一种具体实现类的对象
  • 可以使用自定义类继承DeaflutTask
    • 在方法上使用@TaskAction注解,表示任务运行时调用的方法
    • 使用@Input表示对任务的输入参数
    • 使用@OutputFile表示任务输出文件
    • 使用inputs,outputs直接设置任务输入/输出项
    • 一个任务的输出项可以作为另一个任务的输入项 (隐式依赖关系)
class ZipTask extends DefaultTask {

//    @Input
//    @Optional
    // 表示可选
    String from

//    @OutputFile
//    @Optional
    // 表示可选
    File out

    ZipTask() {
        outputs.upToDateWhen {
            false//  增量构建,每次都会开启,不会跳过任务
        }
    }

    @TaskAction
    void fun() {
        println " @TaskAction fun()"
        println from
        println out

        //文件进行操作
        //inputs.files.first()
        println inputs.files.singleFile
        def inFile = inputs.files.singleFile

        def file = outputs.files.singleFile
        file.createNewFile()
        file.text = inFile.text
    }
}

task myTask(type: ZipTask) {
    from = "a/b/c" // 输入
    out = file("test.txt") // 输出
    inputs.file file('build.gradle')
    outputs.file file('test.txt')
}

使用自定义任务,实现zip打包packageDebug输出的内容

task zip(type: Zip) {
    archiveName "outputs.zip"// 输出的文件名字
    destinationDir file("${buildDir}/custom")// 输出的文件存放的文件夹
    from "${buildDir}/outputs"// 输入的文件
}

压缩packageDebug任务的输出文件

afterEvaluate {

    println tasks.getByName("packageDebug")

    task zip(type: Zip) {
        archiveName "outputs2.zip"// 输出的文件名字
        destinationDir file("${buildDir}/custom")// 输出的文件存放的文件夹
        from tasks.getByName("packageDebug").outputs.files// 输入的文件
        tasks.getByName("packageDebug").outputs.files.each {
            println it
        }
    }
}

gradle_2102">gradle钩子函数

  • gradle在生命周期三个阶段都设置了相应的钩子函数调用
  • 使用钩子函数,处理自定义的构建
    • 初始化阶段:gradle.settingsEvaluated和gradle.projectsLoaded。(在settings.gradle中生效)
    • 配置阶段:project.beforeEvaluate和project.afterEvaluate;gradle.beforeProject、gradle.afterProject及gradle.taskGraph.taskGraph.whenReady
    • 执行阶段:gradle.taskGraph.beforeTask和gradle.taskGraph.afterTask

gradle_2110">gradle构建监听

  • gradle可以设置监听,对各阶段都有相应的回调处理
    • gradle.addProjectEvaluationListener
    • gradle.addBuildListener
    • gradle.addListener:TaskExecutionGraphListener (任务执行图监听),TaskExecutionListener(任务执行监听),TaskExecutionListener、TaskActionListener、StandardOutputListener …

Daemon(守护进程)

  • 项目启动时,会开启一个client,然后启动一个daemon,通过client向daemon收发请求,项目关闭,client关闭,daemon保持启动,有类似项目再次部署时,会直接通过新的client访问已经启动的daemon,所以速度很快,默认daemon不使用3小时后关闭;不同项目兼容性考虑,也可使用–no-daemon 启动项目,就没有速度优势了
  • 手动停止daemon:gradle wrapper --stop

Project

  • build.gradle在配置阶段会生成project实例,在build.gradle中直接调用方法或属性,实则是调用当前工程project对象的方法或属性
  • 使用Project提供的Api,在多项目构建设置游刃有余
    • project(‘:app’){} 指定的project (这里是app) 配置
    • allprojects{} 所有的project配置
    • subprojects{} 所有的子project配置
    • buildscript {} 此项目配置构建脚本类路径

在根build.gradle文件中,给app模块进行配置

project(':app') {
//    plugins {
//        id 'com.android.application'
//        id 'kotlin-android'
//    }

    apply plugin: 'com.android.application'
    apply plugin: 'kotlin-android'

    android {
        compileSdkVersion 30
        buildToolsVersion "30.0.2"

        defaultConfig {
            applicationId "com.dongnaoedu.gradledemo"
            minSdkVersion 28
            targetSdkVersion 30
            versionCode 1
            versionName "1.0"

            testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
        }

        buildTypes {
            release {
                minifyEnabled false
                proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
            }
        }
        compileOptions {
            sourceCompatibility JavaVersion.VERSION_1_8
            targetCompatibility JavaVersion.VERSION_1_8
        }
        kotlinOptions {
            jvmTarget = '1.8'
        }
    }

    dependencies {
        implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
        implementation 'androidx.core:core-ktx:1.2.0'
        implementation 'androidx.appcompat:appcompat:1.1.0'
        implementation 'com.google.android.material:material:1.1.0'
        implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
        testImplementation 'junit:junit:4.+'
        androidTestImplementation 'androidx.test.ext:junit:1.1.1'
        androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
    }

}

http://www.niftyadmin.cn/n/869190.html

相关文章

动态修改 webconfig 节点constring_DolphinDB集群如何扩展节点和存储

1. 概述当系统准备上线前&#xff0c;我们会评估和规划硬件平台的容量&#xff0c;并且尽可能的留出余量。可是现实往往是不能预料的&#xff0c;随着业务的扩张&#xff0c;系统的数据容量和计算能力都会变得不堪重负&#xff0c;我们就不得不面对一个问题&#xff1a;如何为现…

android编译期注解-模仿butterknife

注解是java中的功能&#xff0c;android在实现注解、注解处理器时&#xff0c;需要创建java library 让app模块依赖annotation和annotationprocess这两个模块 进入app模块的build.gradle&#xff0c;修改依赖&#xff0c;将annotationprocess修改为注解处理器依赖 将我们创…

python 多线程 全局锁_[ Python - 11 ] 多线程及GIL全局锁

1. GIL是什么&#xff1f;首先需要明确的一点是GIL并不是python的特性&#xff0c; 它是在实现python解析器(Cpython)时所引入的一个概念。而Cpython是大部分环境下默认的python执行环境&#xff0c;要明确一点&#xff1a;GIL并不是python的特性&#xff0c;python完全可以不依…

ASM使用小抄

0x00 参考&#xff1a;ASM教程 0x01 Core Api 生成新的类 核心接口是ClassVisitor&#xff0c;其中方法调用的顺序为 visit [visitSource][visitModule][visitNestHost][visitPermittedSubclass][visitOuterClass] (visitAnnotation |visitTypeAnnotation |visitAttribute…

python123第七周测验_嵩天老师python123测验1: Python基本语法元素 (第1周)

选择题**Guido van Rossum正式对外发布Python版本的年份是&#xff1a;‪‬‪‬‪‬‪‬‪‬‮‬‪‬‫‬‪‬‪‬‪‬‪‬‪‬‮‬‪‬‮‬‪‬‪‬‪‬‪‬‪‬‮‬‫‬‫‬‪‬‪‬‪‬‪‬‪‬‮‬‪‬‪‬‪‬‪‬‪‬‪‬‪‬‮‬‭‬‫‬‪‬‪‬‪‬‪‬‪‬‮‬‫‬‪…

python numpy库打不开_python-numpy库的使用

import numpy as npnumpy 的属性array np.array([[1,2,3],[4,5,6],[7,8,9]])print(array)print(array.ndim) #维度print(array.shape) #形状print(array.size) #大小print(array.dtype) #元素类型arraya np.array([1,23,30],dtypenp.float)print(a.dtype)c np.array([1,2,3]…

确定最佳聚类数matlab代码_聚类(一):K-means、层次、DBSCAN、均值漂移、K-Means 与 KNN...

文章目录一、K-means简介1.1 K-means简介1.1.1 K值的确定1.1.2 K-means 成本函数(利用SSE选择k)1.2 层次聚类1.3 DBSCAN - 基于密度的聚类算法1.3.1 简介1.3.2 具体步骤1.4 均值漂移聚类1.4.1 简介1.4.2 步骤二、代码2.1 原理推导2.2 make_blobs三、总结3.1 K-Means 与 KNN3.2 …

在python中、x=x+1是合法语句吗_在Python中i += x和i = i + x什么时候不等

增强型赋值语句是经常被使用到的&#xff0c;因为从各种学习渠道中&#xff0c;我们能够得知 i 1 的效率往往要比 i i 1 更高一些(这里以 为例&#xff0c;实际上增强型赋值语句不仅限于此)。所以我们会乐此不疲的在任何能够替换普通赋值语句的地方使用增量型赋值语句&…