深入学习-Gradle-自动化构建技术(二)Groovy-筑基

2024-06-23 13:28

本文主要是介绍深入学习-Gradle-自动化构建技术(二)Groovy-筑基,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

但是,如果你这个类或变量要用于其它模块的,建议不要使用 def,还是应该使用 Java 中的那种强类型定义方式,因为使用强类型的定义方式,它不能动态转换为其它类型,它能够保证外界传递进来的值一定是正确的。如果你这个变量要被外界使用,而你却使用了 def 类型来定义它,那外界需要传递给你什么才是正确的呢?这样会使调用方很疑惑。

如果此时我们在后面的代码中改变上图中 x1 的值为 String 类型,那么 x1 又会被编译器推断为 String 类型,如下图所示:

于是我们可以猜测到,其实使用 def 关键字定义出来的变量就是 Obejct 类型。

Groovy 中的字符串

Groovy 中的字符串与 Java 中的字符串有比较大的不同,所以这里我们需要着重了解一下。

Groovy 中的字符串除了继承了 Java 中传统 String 的使用方式之前,还 新增 了一个 GString 类型,它的使用方式至少有七、八种,但是常用的有三种定义方式。此外,在 GString 中新增了一系列的操作符,这能够让我们对 String 类型的变量有 更便捷的操作。最后,在 GString 中还 新增 了一系列好用的 API,我们也需要着重学习一下。

Groovy 中常用的三种字符串定义方式

在 Groovy 中有 三种常用 的字符串定义方式,如下所示:

  • 1)、单引号 ‘’ 定义的字符串
  • 2)、双引号 “” 定义的字符串
  • 3)、三引号 ‘“”’ 定义的字符串

首先,需要说明的是,‘不管是单引号、双引号还是三引号,它们的类型都是 java.lang.String’。

那么,单引号与三引号的区别是什么呢?

既生瑜何生亮,其实不然。当我们编写的单引号字符串中有转义字符的时候,需要添加 ‘’,并且,当字符串需要具备多行格式的时候,强行将单引号字符串分成多行格式会变成由 ‘+’ 号组成的字符串拼接格式

那么,双引号定义的变量又与单引号、三引号有什么区别呢?

双引号不同与单、三引号,它定义的是一个可扩展的变量。这里我们先看看两种双引号的使用方式,如下图所示:

在上图中,第一个定义的 author 字符串就是常规的 String 类型的字符串,而下面定义的 study 字符串就是可扩展的字符串,因为它里面使用了 ‘${author}’ 的方式引用了 author 变量的内容。而且,从其最后的类型输出可以看到,可扩展的类型就是 ‘org.codehaus.groovy.runtime.GStringImpl’ 类型的。

需要注意的是,可扩展的字符串是可以扩展成为任意的表达式,例如数学运算,如下图所示:

有了 Groovy 的这种可扩展的字符串,我们就可以 避免 Java 中字符串的拼接操作,提升 Java 程序运行时的性能

那么,既然有 String 和 GString 两种类型的字符串,它们在相互赋值的场景下需要不需要先强转再赋值呢?

这里,我们可以写一个 小栗子来看看实际的情况,如下图所示:

可以看到,我们将 success 字符串传入了 come 方法,但是最终得到的类型为 result,所以,可以说明 编译器可以帮我们自动在 String 和 GString 之间相互转换,我们在编写的时候并不需要太过关注它们的区别

2、Groovy 闭包(Closure)

闭包的本质其实就是一个代码块,闭包的核心内容可以归结为如下三点:

  • 1)、闭包概念

  • 定义

  • 闭包的调用

  • 2)、闭包参数

  • 普通参数

  • 隐式参数

  • 3)、闭包返回值

  • 总是有返回值

闭包的调用

clouser.call()
clouser()
def xxx = { paramters -> code }
def xxx = { 纯 code }

从 C/C++ 语言的角度看,闭包和函数指针很像,闭包可以通过 .call 方法来调用,也可以直接调用其构造函数,代码如下所示:

闭包对象.call(参数)
闭包对象(参数)

如果闭包没定义参数的话,则隐含有一个参数,这个参数名字叫 it,和 this 的作用类 似。it 代表闭包的参数。表示闭包中没有参数的示例代码:

def noParamClosure = { -> true }

注意点:省略圆括号

函数最后一个参数都是一个闭包,类似于回调函数的用法,代码如下所示:

task JsonChao {
doLast ({
println “love is peace~”
}
})

// 似乎好像doLast会立即执行一样
task JsonChao {
doLast {
println “love is peace~”
}
}

闭包的用法

闭包的常见用法有如下 四种:

  • 1)、与基本类型的结合使用。
  • 2)、与 String 类的结合使用。
  • 3)、与数据结构的结合使用。
  • 4)、与文件等结合使用。

闭包进阶

  • 1)、闭包的关键变量

  • this

  • owner

  • delegate

  • 2)、闭包委托策略

闭包的关键变量
this 与 owner、delegate

其差异代码如下代码所示:

def scrpitClouser = {
// 代表闭包定义处的类
printlin “scriptClouser” this:" + this
// 代表闭包定义处的类或者对象
printlin “scriptClouser” this:" + owner
// 代表任意对象,默认与 ownner 一直
printlin “scriptClouser” this:" + delegate
}

// 输出都是 scrpitClouse 对象
scrpitClouser.call()

def nestClouser = {
def innnerClouser = {
// 代表闭包定义处的类
printlin “scriptClouser” this:" + this
// 代表闭包定义处的类或者对象
printlin “scriptClouser” this:" + owner
// 代表任意对象,默认与 ownner 一直
printlin “scriptClouser” this:" + delegate
}
innnerClouser.call()
}

// this 输出的是 nestClouser 对象,而 owner 与 delegate 输出的都是 innnerClouser 对象
nestClouser.call()

可以看到,如果我们直接在类、方法、变量中定义一个闭包,那么这三种关键变量的值都是一样的,但是,如果我们在闭包中又嵌套了一个闭包,那么,this 与 owner、delegate 的值就不再一样了。换言之,this 还会指向我们闭包定义处的类或者实例本身,而 owner、delegate 则会指向离它最近的那个闭包对象

delegate 与 this、owner 的差异

其差异代码如下代码所示:

def nestClouser = {
def innnerClouser = {
// 代表闭包定义处的类
printlin “scriptClouser” this:" + this
// 代表闭包定义处的类或者对象
printlin “scriptClouser” this:" + owner
// 代表任意对象,默认与 ownner 一致
printlin “scriptClouser” this:" + delegate
}

// 修改默认的 delegate
innnerClouser.delegate = p
innnerClouser.call()
}

nestClouser.call()

可以看到,delegate 的值是可以修改的,并且仅仅当我们修改 delegate 的值时,delegate 的值才会与 ownner 的值不一样

闭包的委托策略

其示例代码如下所示:

def stu = new Student()
def tea = new Teacher()
stu.pretty.delegate = tea
// 要想使 pretty 闭包的 delegate 修改生效,必须选择其委托策略为 Closure.DELEGATE_ONLY,默认是 Closure.OWNER_FIRST。
stu.pretty.resolveStrategy = Closure.DELEGATE_ONLY
println stu.toString()

需要注意的是,要想使上述 pretty 闭包的 delegate 修改生效,必须选择其委托策略为 Closure.DELEGATE_ONLY,默认是 Closure.OWNER_FIRST 的。

3、Groovy 数据结构

Groovy 常用的数据结构有如下 四种:

  • 1)、数组
  • 2)、List
  • 3)、Map
  • 4)、Range

数组的使用和 Java 语言类似,最大的区别可能就是定义方式的扩展,如下代码所示:

// 数组定义
def array = [1, 2, 3, 4, 5] as int[]
int[] array2 = [1, 2, 3, 4, 5]

下面,我们看看其它三种数据结构。

1、List

即链表,其底层对应 Java 中的 List 接口,一般用 ArrayList 作为真正的实现类,List 变量由[]定义,其元素可以是任何对象

链表中的元素可以通过索引存取,而且 不用担心索引越界。如果索引超过当前链表长度,List 会自动往该索引添加元素。下面,我们看看 Map 最常使用的几个操作。

1)、排序

def test = [100, “hello”, true]
// 左移位表示向List中添加新元素
test << 200
// list 定义
def list = [1, 2, 3, 4, 5]
// 排序
list.sort()
// 使用自己的排序规则
sortList.sort { a, b ->
a == b ?0 :
Math.abs(a) < Math.abs(b) ? 1 : -1
}

2)、添加

// 添加
list.add(6)
list.leftShift(7)
list << 8

3)、删除

// 删除
list.remove(7)
list.removeAt(7)
list.removeElement(6)
list.removeAll { return it % 2 == 0 }

4)、查找

// 查找
int result = findList.find { return it % 2 == 0 }
def result2 = findList.findAll { return it % 2 != 0 }
def result3 = findList.any { return it % 2 != 0 }
def result4 = findList.every { return it % 2 == 0 }

5)、获取最小值、最大值

// 最小值、最大值
list.min()
list.max(return Math.abs(it))

6)、统计满足条件的数量

// 统计满足条件的数量
def num = findList.count { return it >= 2 }

Map

表示键-值表,其 底层对应 Java 中的 LinkedHashMap

Map 变量由[:]定义,冒号左边是 key,右边是 Value。key 必须是字符串,value 可以是任何对象。另外,key 可以用 ‘’ 或 “” 包起来,也可以不用引号包起来。下面,我们看看 Map 最常使用的几个操作。

1)、存取

其示例代码如下所示:

aMap.keyName
aMap[‘keyName’]
aMap.anotherkey = “i am map”
aMap.anotherkey = [a: 1, b: 2]

2)、each 方法

如果我们传递的闭包是一个参数,那么它就把 entry 作为参数。如果我们传递的闭包是 2 个参数,那么它就把 key 和 value 作为参数。

def result = “”
[a:1, b:2].each { key, value ->
result += “ k e y key keyvalue”
}

assert result == “a1b2”

def socre = “”
[a:1, b:2].each { entry ->
result += entry
}

assert result == “a=1b=2”

3)、eachWithIndex 方法

如果闭包采用两个参数,则将传递 Map.Entry 和项目的索引(从零开始的计数器);否则,如果闭包采用三个参数,则将传递键,值和索引。

def result = “”
[a:1, b:3].eachWithIndex { key, value, index -> result += “ i n d e x ( index( index(key$value)” }
assert result == “0(a1)1(b3)”

def result = “”
[a:1, b:3].eachWithIndex { entry, index -> result += “ i n d e x ( index( index(entry)” }
assert result == “0(a=1)1(b=3)”

4)、groupBy 方法

按照闭包的条件进行分组,代码如下所示:

def group = students.groupBy { def student ->
return student.value.score >= 60 ? ‘及格’ : ‘不及格’
}

5)、findAll 方法

它有两个参数,findAll 会将 Key 和 Value 分别传进 去。并且,如果 Closure 返回 true,表示该元素是自己想要的,如果返回 false 则表示该元素不是自己要找的。

Range

表示范围,它其实是 List 的一种拓展。其由 begin 值 + 两个点 + end 值表示。如果不想包含最后一个元素,则 begin 值 + 两个点 + < + end 表示。我们可以通过 aRange.from 与 aRange.to 来获对应的边界元素

如果需要了解更多的数据结构操作方法,我们可以直接查 Groovy API 详细文档 即可。

4、Groovy 面向对象

如果不声明 public/private 等访问权限的话,Groovy 中类及其变量默认都是 public 的

1)、元编程(Groovy 运行时)

Groovy 运行时的逻辑处理流程图如下所示:

为了更好的讲解元编程的用法,我们先创建一个 Person 类并调用它的 cry 方法,代码如下所示:

// 第一个 groovy 文件中
def person = new Person(name: ‘Qndroid’, age: 26)
println person.cry()

// 第二个 groovy 文件中
class Person implements Seri​
alizable {

String name

Integer age

def increaseAge(Integer years) {
this.age += years
}

/**

  • 一个方法找不到时,调用它代替
  • @param name
  • @param args
  • @return
    */
    def invokeMethod(String name, Object args) {

return “the method is ${name}, the params is ${args}”
}

def methodMissing(String name, Object args) {

return “the method ${name} is missing”
}
}

为了实现元编程,我们需要使用 metaClass,具体的使用示例如下所示:

ExpandoMetaClass.enableGlobally()
//为类动态的添加一个属性
Person.metaClass.sex = ‘male’
def person = new Person(name: ‘Qndroid’, age: 26)
println person.sex
person.sex = ‘female’
println “the new sex is:” + person.sex
//为类动态的添加方法
Person.metaClass.sexUpperCase = { -> sex.toUpperCase() }
def person2 = new Person(name: ‘Qndroid’, age: 26)
println person2.sexUpperCase()
//为类动态的添加静态方法
Person.metaClass.static.createPerson = {
String name, int age -> new Person(name: name, age: age)
}
def person3 = Person.createPerson(‘renzhiqiang’, 26)
println person3.name + " and " + person3.age

需要注意的是通过类的 metaClass 来添加元素的这种方式每次使用时都需要重新添加,幸运的是,我们可以在注入前调用全局生效的处理,代码如下所示:

ExpandoMetaClass.enableGlobally()
// 在应用程序初始化的时候我们可以为第三方类添加方法
Person.metaClass.static.createPerson = { String name,
int age ->
new Person(name: name, age: age)
}

2)、脚本中的变量和作用域

对于每一个 Groovy 脚本来说,它都会生成一个 static void main 函数,main 函数中会调用一个 run 函数,脚本中的所有代码则包含在 run 函数之中。我们可以通过如下的 groovyc 命令用于将编译得到的 class 文件拷贝到 classes 文件夹下:

// groovyc 是 groovy 的编译命令,-d classes 用于将编译得到的 class 文件拷贝到 classes 文件夹 下
groovyc -d classes test.groovy

当我们在 Groovy 脚本中定义一个变量时,由于它实际上是在 run 函数中创建的,所以脚本中的其它方法或其他脚本是无法访问它的。这个时候,我们需要使用 @Field 将当前变量标记为成员变量,其示例代码如下所示:

import groovy.transform.Field;

@Field author = JsonChao

四、文件处理

1、常规文件处理

1)、读文件

eachLine 方法

我们可以使用 eachLine 方法读该文件中的每一行,它唯一的参数是一个 Closure,Closure 的参数是文件每一行的内容。示例代码如下所示:

def file = new File(文件名)
file.eachLine{ String oneLine ->
println oneLine
}

def text = file.getText()
def text2 = file.readLines()

file.eachLine { oneLine, lineNo ->
println “${lineNo} ${oneLine}”
}

然后,我们可以使用 ‘targetFile.bytes’ 直接得到文件的内容。

使用 InputStream

此外,我们也可以通过流的方式进行文件操作,如下代码所示:

//操作 ism,最后记得关掉
def ism = targetFile.newInputStream()
// do sth
ism.close

使用闭包操作 inputStream

利用闭包来操作 inputStream,其功能更加强大,推荐使用这种写法,如下所示:

targetFile.withInputStream{ ism ->
// 操作 ism,不用 close。Groovy 会自动替你 close
}

2)、写文件

关于写文件有两种常用的操作形式,即通过 withOutputStream/withInputStream 或 withReader/withWriter 的写法。示例代码如下所示:

通过 withOutputStream/、withInputStream copy 文件

def srcFile = new File(源文件名)
def targetFile = new File(目标文件名) targetFile.withOutputStream{ os->
srcFile.withInputStream{ ins->
os << ins //利用 OutputStream 的<<操作符重载,完成从 inputstream 到 OutputStream //的输出
}
}

通过 withReader、withWriter copy 文件

def copy(String sourcePath, String destationPath) {
try {
//首先创建目标文件
def desFile = new File(destationPath)
if (!desFile.exists()) {
desFile.createNewFile()
}

//开始copy
new File(sourcePath).withReader { reader ->
def lines = reader.readLines()
desFile.withWriter { writer ->
lines.each { line ->
writer.append(line + “\r\n”)
}
}
}
return true
} catch (Exception e) {
e.printStackTrace()
}
return false
}

此外,我们也可以通过 withObjectOutputStream/withObjectInputStream 来保存与读取 Object 对象。示例代码如下所示:

保存对应的 Object 对象到文件中

def saveObject(Object object, String path) {
try {
//首先创建目标文件
def desFile = new File(path)
if (!desFile.exists()) {
desFile.createNewFile()
}
desFile.withObjectOutputStream { out ->
out.writeObject(object)
}
return true
} catch (Exception e) {
}
return false
}

从文件中读取 Object 对象

def readObject(String path) {
def obj = null
try {
def file = new File(path)
if (file == null || !file.exists()) return null
//从文件中读取对象
file.withObjectInputStream { input ->
obj = input.readObject()
}
} catch (Exception e) {

}
return obj
}

2、XML 文件操作

1)、获取 XML 数据

首先,我们定义一个包含 XML 数据的字符串,如下所示:

final String xml = ‘’’



疯狂Android讲义 李刚 第一行代码 郭林 Android开发艺术探索 任玉刚 Android源码设计模式 何红辉 Vue从入门到精通 李刚 '''

然后,我们可以 使用 XmlSlurper 来解析此 xml 数据,代码如下所示:

def xmlSluper = new XmlSlurper()
def response = xmlSluper.parseText(xml)

// 通过指定标签获取特定的属性值
println response.value.books[0].book[0].title.text()
println response.value.books[0].book[0].author.text()
println response.value.books[1].book[0].@available

def list = []
response.value.books.each { books ->
//下面开始对书结点进行遍历
books.book.each { book ->
def author = book.author.text()
if (author.equals(‘李刚’)) {
list.add(book.title.text())
}
}
}
println list.toListString()

2)、获取 XML 数据的两种遍历方式

获取 XML 数据有两种遍历方式:深度遍历 XML 数据 与 广度遍历 XML 数据,下面我们看看它们各自的用法,如下所示:

深度遍历 XML 数据

def titles = response.depthFirst().findAll { book ->
return book.author.text() == ‘李刚’ ? true : false
}
println titles.toListString()

广度遍历 XML 数据

def name = response.value.books.children().findAll { node ->
node.name() == ‘book’ && node.@id == ‘2’
}.collect { node ->
return node.title.text()
}

在实际使用中,我们可以 利用 XmlSlurper 求获取 AndroidManifest.xml 的版本号(versionName),代码如下所示:

def androidManifest = new XmlSlurper().parse(“AndroidManifest.xml”) println androidManifest[‘@android:versionName’]
或者
println androidManifest.@‘android:versionName’

3)、生成 XML 数据

除了使用 XmlSlurper 解析 XML 数据之外,我们也可以 使用 xmlBuilder 来创建 XML 文件,如下代码所示:

/**

  • 生成 xml 格式数据

Java
Groovy
JavaScript

*/
def sw = new StringWriter()
// 用来生成 xml 数据的核心类
def xmlBuilder = new MarkupBuilder(sw)
// 根结点 langs 创建成功
xmlBuilder.langs(type: ‘current’, count: ‘3’,
mainstream: ‘true’) {
//第一个 language 结点
language(flavor: ‘static’, version: ‘1.5’) {
age(‘16’)
}
language(flavor: ‘dynamic’, version: ‘1.6’) {
age(‘10’)
}
language(flavor: ‘dynamic’, version: ‘1.9’, ‘JavaScript’)
}

// println sw

def langs = new Langs()
xmlBuilder.langs(type: langs.type, count: langs.count,
mainstream: langs.mainstream) {
//遍历所有的子结点
langs.languages.each { lang ->
language(flavor: lang.flavor,
version: lang.version, lang.value)
}
}

println sw

// 对应 xml 中的 langs 结点
class Langs {
String type = ‘current’
int count = 3
boolean mainstream = true
def languages = [
new Language(flavor: ‘static’,
version: ‘1.5’, value: ‘Java’),
new Language(flavor: ‘dynamic’,
version: ‘1.3’, value: ‘Groovy’),
new Language(flavor: ‘dynamic’,
version: ‘1.6’, value: ‘JavaScript’)
]
}
//对应xml中的languang结点
class Language {
String flavor
String version
String value
}

4)、Groovy 中的 json

我们可以 使用 Groovy 中提供的 JsonSlurper 类去替代 Gson 解析网络响应,这样我们在写插件的时候可以避免引入 Gson 库,其示例代码如下所示:

def reponse =
getNetworkData(
‘http://yuexibo.top/yxbApp/course_detail.json’)

最后

小编这些年深知大多数初中级Android工程师,想要提升自己,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助

因此我收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人

都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

资料⬅专栏获取
rsion: ‘1.5’, value: ‘Java’),
new Language(flavor: ‘dynamic’,
version: ‘1.3’, value: ‘Groovy’),
new Language(flavor: ‘dynamic’,
version: ‘1.6’, value: ‘JavaScript’)
]
}
//对应xml中的languang结点
class Language {
String flavor
String version
String value
}

4)、Groovy 中的 json

我们可以 使用 Groovy 中提供的 JsonSlurper 类去替代 Gson 解析网络响应,这样我们在写插件的时候可以避免引入 Gson 库,其示例代码如下所示:

def reponse =
getNetworkData(
‘http://yuexibo.top/yxbApp/course_detail.json’)

最后

小编这些年深知大多数初中级Android工程师,想要提升自己,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助

因此我收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。

[外链图片转存中…(img-OIFFsLTX-1719112619881)]一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人

都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

资料⬅专栏获取

这篇关于深入学习-Gradle-自动化构建技术(二)Groovy-筑基的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1087280

相关文章

51单片机学习记录———定时器

文章目录 前言一、定时器介绍二、STC89C52定时器资源三、定时器框图四、定时器模式五、定时器相关寄存器六、定时器练习 前言 一个学习嵌入式的小白~ 有问题评论区或私信指出~ 提示:以下是本篇文章正文内容,下面案例可供参考 一、定时器介绍 定时器介绍:51单片机的定时器属于单片机的内部资源,其电路的连接和运转均在单片机内部完成。 定时器作用: 1.用于计数系统,可

问题:第一次世界大战的起止时间是 #其他#学习方法#微信

问题:第一次世界大战的起止时间是 A.1913 ~1918 年 B.1913 ~1918 年 C.1914 ~1918 年 D.1914 ~1919 年 参考答案如图所示

Spring Cloud:构建分布式系统的利器

引言 在当今的云计算和微服务架构时代,构建高效、可靠的分布式系统成为软件开发的重要任务。Spring Cloud 提供了一套完整的解决方案,帮助开发者快速构建分布式系统中的一些常见模式(例如配置管理、服务发现、断路器等)。本文将探讨 Spring Cloud 的定义、核心组件、应用场景以及未来的发展趋势。 什么是 Spring Cloud Spring Cloud 是一个基于 Spring

[word] word设置上标快捷键 #学习方法#其他#媒体

word设置上标快捷键 办公中,少不了使用word,这个是大家必备的软件,今天给大家分享word设置上标快捷键,希望在办公中能帮到您! 1、添加上标 在录入一些公式,或者是化学产品时,需要添加上标内容,按下快捷键Ctrl+shift++就能将需要的内容设置为上标符号。 word设置上标快捷键的方法就是以上内容了,需要的小伙伴都可以试一试呢!

乐鑫 Matter 技术体验日|快速落地 Matter 产品,引领智能家居生态新发展

随着 Matter 协议的推广和普及,智能家居行业正迎来新的发展机遇,众多厂商纷纷投身于 Matter 产品的研发与验证。然而,开发者普遍面临技术门槛高、认证流程繁琐、生产管理复杂等诸多挑战。  乐鑫信息科技 (688018.SH) 凭借深厚的研发实力与行业洞察力,推出了全面的 Matter 解决方案,包含基于乐鑫 SoC 的 Matter 硬件平台、基于开源 ESP-Matter SDK 的一

AssetBundle学习笔记

AssetBundle是unity自定义的资源格式,通过调用引擎的资源打包接口对资源进行打包成.assetbundle格式的资源包。本文介绍了AssetBundle的生成,使用,加载,卸载以及Unity资源更新的一个基本步骤。 目录 1.定义: 2.AssetBundle的生成: 1)设置AssetBundle包的属性——通过编辑器界面 补充:分组策略 2)调用引擎接口API

Javascript高级程序设计(第四版)--学习记录之变量、内存

原始值与引用值 原始值:简单的数据即基础数据类型,按值访问。 引用值:由多个值构成的对象即复杂数据类型,按引用访问。 动态属性 对于引用值而言,可以随时添加、修改和删除其属性和方法。 let person = new Object();person.name = 'Jason';person.age = 42;console.log(person.name,person.age);//'J

一份LLM资源清单围观技术大佬的日常;手把手教你在美国搭建「百万卡」AI数据中心;为啥大模型做不好简单的数学计算? | ShowMeAI日报

👀日报&周刊合集 | 🎡ShowMeAI官网 | 🧡 点赞关注评论拜托啦! 1. 为啥大模型做不好简单的数学计算?从大模型高考数学成绩不及格说起 司南评测体系 OpenCompass 选取 7 个大模型 (6 个开源模型+ GPT-4o),组织参与了 2024 年高考「新课标I卷」的语文、数学、英语考试,然后由经验丰富的判卷老师评判得分。 结果如上图所

大学湖北中医药大学法医学试题及答案,分享几个实用搜题和学习工具 #微信#学习方法#职场发展

今天分享拥有拍照搜题、文字搜题、语音搜题、多重搜题等搜题模式,可以快速查找问题解析,加深对题目答案的理解。 1.快练题 这是一个网站 找题的网站海量题库,在线搜题,快速刷题~为您提供百万优质题库,直接搜索题库名称,支持多种刷题模式:顺序练习、语音听题、本地搜题、顺序阅读、模拟考试、组卷考试、赶快下载吧! 2.彩虹搜题 这是个老公众号了 支持手写输入,截图搜题,详细步骤,解题必备

持久层 技术选型如何决策?JPA,Hibernate,ibatis(mybatis)

转自:http://t.51jdy.cn/thread-259-1-1.html 持久层 是一个项目 后台 最重要的部分。他直接 决定了 数据读写的性能,业务编写的复杂度,数据结构(对象结构)等问题。 因此 架构师在考虑 使用那个持久层框架的时候 要考虑清楚。 选择的 标准: 1,项目的场景。 2,团队的技能掌握情况。 3,开发周期(开发效率)。 传统的 业务系统,通常业