Kotlin程序设计 扩展篇(一)

news2024/11/15 17:35:31

image-20240113154815774

Kotlin程序设计(扩展一)

**注意:**开启本视频学习前,需要先完成以下内容的学习:

  • 请先完成《Kotlin程序设计》视频教程。
  • 请先完成《JavaSE》视频教程。

Kotlin在设计时考虑到了与Java的互操作性,现有的Java代码可以自然地调用Kotlin代码,而Kotlin代码也可以轻松兼容Java的调用。在本扩展篇中,我们会讲解如何通过Kotlin调用Java代码。

Kotlin调用Java

我们先从最基本的内容说起,现在需要让Kotlin与Java互相兼容,并不是直接就可以使用的,我们还要遵循某些约定才可以使得Java兼容Kotlin的语法。

类的定义和使用

在Java中,最关键的就是类,我们来看看如何在Kotlin中进行使用。

我们在Java中定义的类型,可以非常轻松地被Kotlin使用,比如下面这个由Java语言定义的类型:

public class Student {
  	int age;
    String name;
}

在Kotlin中,我们可以直接使用这个类,就像是在Kotlin中定义的那样:

fun main() {
    val student: Student = Student()   //直接使用Java中的类型,无缝衔接
    student.name = "小明"
    println(student.name)  //这里得到的Java中的String类型,可以直接当做Kotlin中的使用
}

以及Kotlin中我们提到的一些基本类型,都可以与Java中的基本类型互相转换:

fun main() {
    val student = Student()
    val age: Int = student.age   //Java中的int/Integer对应了Kotlin中的Int
}

几乎Java中所有基本类型在Kotlin中都存在对应的类型,所以说直接转换为Kotlin支持的基本类型也是可以的。

包括我们在类中定义的方法,也可以在Kotlin中被当做函数使用:

public class Student {
    String name;

    public void hello() {
        System.out.println("大家好,我叫" + name);
    }
}
fun main() {
    val student = Student()
    student.name = "小明"
    student.hello()   //函数调用
}

注意,如果方法的返回类型是void,那么它对应的就是Kotlin中的Unit类型。

包括在Java中定义的构造方法,也可以被Kotlin当做构造函数使用,因为它们的语法其实差不多,可以很轻松完成兼容:

public class Student {
    String name;
    
    public Student(String name) {
        this.name = name;
    }
}
fun main() {
    val student = Student("小明")
}

可以看到,这些内容几乎是没有多少学习成本的,包括在Java类中定义的静态内容:

public class Student {
    public static void test() {
        System.out.println("我是测试静态函数");
    }
}

这些静态属性就像使用Kotlin中的伴生对象一样,可以直接通过类名进行调用,这跟Java中是一样的:

fun main() {
    Student.test()
}

还有,由于Kotlin与Java中的关键字存在差异,我们在Java中定义的某些属性名称可能会成为Kt的关键字:

import java.io.InputStream;
public class Student {
    InputStream in;  //Java中没有问题,因为in不是关键字
}
fun main() {
    val student = Student()
  	//在Kotlin中,由于in是关键字,因此我们需要对其进行转义来消除冲突
  	//使用``字符来完成转义
    student.`in` = FileInputStream("C://")
}

包括Java中的可变参数,也是可以直接兼容的:

public class Student {
    public void test(String... args) {
        
    }
}

image-20240113205404890

我们也可以直接继承Java中提供的类型:

public class Student {

}
class ArtStudent: Student()   //语法与之前是一样的

在后续的学习中,我们再来继续认识更多高级的内容。

Getter和Setter

在Kotlin基础教程中我们说到,类中的成员属性可以具有自己的Getter和Setter函数,比如:

class Student {
    var name: String = ""
        get() = field
        set(value) {
            field = value
        }
}

这样我们就可以实现对于这个变量赋值和获取的进一步控制,比如我们希望在赋值时打印内容:

var name: String = ""
    get() = field
    set(value){
        println("我被赋值了!")   //由于get和set本质上编译后是函数,因此可以自定义
        field = value
    }

而我们知道,在Java中,一个类的属性并不能像这样去编写:

public class Student {
    String name;   //只能定义一个变量,非常简单
}

我们可以对Java中的这个属性进行封装,使得其支持像Kotlin中那样存在Getter和Setter函数:

public class Student {
    private String name;   //将name属性private掉

    public String getName() {  //自定义Get和Set方法设置name属性
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
fun main() {
    val student = Student()
    student.name = "小明"
    println(student.name)
}

这样编写之后,我们同样可以在Kotlin中直接使用对应属性的名称进行访问,但是这本质上是通过其Get和Set函数来完成的,在获取属性时,会调用getName()方法得到对应的结果,设置同理。

注意:这个Get和Set必须遵循命名规则,比如这里我们要为name属性添加Getter方法,那么必须要命名为get + Name这样的名称,来表示对name属性的命名,必须以get开头,而Setter必须以set开头。

如果返回值类型是一个Boolean类型,那么Getter方法名称需要以is开头,而Setter同上。

注意,由于此时name属性由于存在访问权限控制,无法被外部访问,如果我们去掉Setter或是Getter函数,将导致变量只能被赋值或是不可用,比如去掉set方法后:

image-20240113161702594

注意: 如果直接去掉Getter方法,无论是否保留Setter方法,都会导致这个变量不可用,因为Kotlin不支持仅set-only属性。

空安全处理

由于在Java中的任何引用类型值都可能是null,这使得Kotlin对来自Java的对象进行空安全检测不太方便。因此,对于Java声明的类型会在Kotlin中以特定方式处理,我们称为平台类型。对于这种类型,空检查是放宽的,因此它们的安全保证与Java相同,也就是说部分情况下不会进行空安全检查。

比如下面这个例子:

public class Student {
    String name;   //默认情况下name属性的值就是null
}
fun main() {
    val student = Student()
  	//此时name在Kotlin中为平台类型,不会进行空安全检查,这里可以编译通过
    println(student.name.uppercase())
}

很明显,上面的代码出现了空指针异常,因为这里没有进行任何的空安全检查。

对于这种平台类型,IDEA会给我们明确指出,比如这里的name属性时String!类型的,它表示这个类型可能是SrtingString?的其中一种:

image-20240113163709872

我们在接受这个属性的时候,由于其特殊性,也可以使用两种:

fun main() {
    val student = Student()
    val name: String = student.name  //直接使用不可空类型接受,但是可能会出错
    val name2: String? = student.name  //直接使用可空类型接受
  	test(student.name)  //函数同样适用此规则
}

fun test(str: String) {  }

如果我们使用了一个不可空类型接受到来自Java的null值,会直接得到一个空指针异常,这可以防止Kotlin的不可空变量持有空值,包括传递函数参数时也同样适用,总的来说,编译器在尽最大努力防止空值在Kotlin程序中传播。

注意,在某些情况下,持有可空注解的Java类型不会表现为平台类型,比如:

public class Student {
    @NotNull String name;  //由JetBrains提供的注解
}

image-20240113164828898

这些注解包括:

  • JetBrains(来自org.jetbrains.annotations包下的@@Nullable@NotNull
  • JSpecify(org.jspecify.nullness
  • Android(com.android.annotationsandroid.support.annotations
  • JSR-305(javax.annotation
  • FindBugs(edu.umd.cs.findbugs.annotations
  • Eclipse(org.eclipse.jdt.annotation
  • Lombok(lombok.NonNull
  • RxJava 3(io.reactivex.rxjava3.annotations

同样的,对于一些泛型类,也存在一些空类型检查问题,这里以List为例:

public class Student {
    List<String> exams;
}
fun main() {
    val student = Student()
    val exams1: MutableList<String?>? = student.exams  //支持多种方式
  	val exams2: MutableList<String?> = student.exams
  	val exams3: List<String?>? = student.exams
  	...
}

可以看到,在我们使用Java中提供的List时,会得到:

image-20240113165654653

这里的(Mutable)List<String!>!包含了很多信息,我们依次来解读一下:

  • 首先Mutable表示这个List可以是可变的也可以是不可变的,因为在Java中并没有明确划分可变或是不可变的数组。
  • 然后这里的类型参数String和List都带有!表示他们都可以是可空类型也可以是不可空类型。

我们同样可以使用非空注解来提醒编译器这里一定不会为null防止被认定为平台类型:

public class Student {
    @NotNull List<String> exams;
}

以及在Java中的数组类型,对应的就是Kotlin中的Array类型:

public class Student {
    String[] exams;
}

image-20240113171513951

由于数组在Java支持协变(这与Kotlin存在不同)因此,这里我们使用Java中的数组时,可以将其当做一个抗变或是协变的String类型进行使用:

fun main() {
    val student = Student()
    val exams1: Array<String>  = student.exams
    val exams2: Array<out String?>  = student.exams
    val exams3: Array<out String?>?  = student.exams
}

这里的到的Array可以是可空也可以是不可空,里面的类型参数String同样可以是可空或是不可空,并且可以是协变也可以是抗变的。

类型对照表

前面我们提到,在Kotlin中存在Java中相应的基本类型,我们在使用Java提供的类型时,可以直接转换使用:

Java类型Java包装类型Kotlin类型
bytejava.lang.Bytekotlin.Byte
shortjava.lang.Shortkotlin.Short
intjava.lang.Integerkotlin.Int
longjava.lang.Longkotlin.Long
charjava.lang.Characterkotlin.Char
floatjava.lang.Floatkotlin.Float
doublejava.lang.Doublekotlin.Double
booleanjava.lang.Booleankotlin.Boolean

注意,虽然Java类型可以映射到Kotlin对应的类型,但是平台类型性质依然保留:

public class Student {
    Integer age;
}

image-20240113180605251

可以看到,对于Java中的包装类型Integer,这里虽然可以直接转换为Int类型,但是它依然可以是Int?或是Int这两种类型。只不过,对于Java中的基本类型来说,由于不存在null这种结果,因此我们可以安全的将其当做不可空类型使用:

public class Student {
    int age;
}

image-20240113181910862

除了这些基本类型之外,实际上Kotlin中还有很多其他类型也可以直接映射:

Java类型Kotlin类型
java.lang.Objectkotlin.Any!
java.lang.Cloneablekotlin.Cloneable!
java.lang.Comparablekotlin.Comparable!
java.lang.Enumkotlin.Enum!
java.lang.annotation.Annotationkotlin.Annotation!
java.lang.CharSequencekotlin.CharSequence!
java.lang.Stringkotlin.String!
java.lang.Numberkotlin.Number!
java.lang.Throwablekotlin.Throwable!

集合类型在Kotlin中可以是只读的或可变的,因此Java的集合映射如下(此表中的所有Kotlin类型都定义在kotlin.collections包中)

Java类型Kotlin只读类型Kotlin可变类型转换平台类型
Iterator<T>Iterator<T>MutableIterator<T>(Mutable)Iterator<T>!
Iterable<T>Iterable<T>MutableIterable<T>(Mutable)Iterable<T>!
Collection<T>Collection<T>MutableCollection<T>(Mutable)Collection<T>!
Set<T>Set<T>MutableSet<T>(Mutable)Set<T>!
List<T>List<T>MutableList<T>(Mutable)List<T>!
ListIterator<T>ListIterator<T>MutableListIterator<T>(Mutable)ListIterator<T>!
Map<K, V>Map<K, V>MutableMap<K, V>(Mutable)Map<K, V>!
Map.Entry<K, V>Map.Entry<K, V>MutableMap.MutableEntry<K,V>(Mutable)Map.(Mutable)Entry<K, V>!

以及数组的映射如下,基本类型的数组会被直接映射为基本类型专用的Array类型:

Java类型Kotlin类型
int[]kotlin.IntArray!
String[]kotlin.Array<(out) String>!

注意,在Java中,这些类型可能存在一些静态属性,如果我们需要调用对应的静态属性,需要使用Java中类型的名称进行调用:

//在Integer中定义的静态方法
public static int parseInt(String s) throws NumberFormatException {
    return parseInt(s,10);
}
fun main() {
    Integer.parseInt("666")  //需要使用原本的名称,而不是转换之后的Int
}

泛型转换

Kotlin的泛型与Java型有点不同,当将Java类型导入Kotlin时,将完成以下转换:

  • Java的通配符转换:
    • Foo<? extends Bar>成为Foo<out Bar!>!
    • Foo<? super Bar>成为Foo<in Bar!>!
public class Student {
    List<? extends Number> data;  //此时泛型上界为Number
    List<? super Integer> data2;  //此时泛型下界为Integer
}

当这些类型在Kotlin中使用时,会自动被划分为协变或抗变类型:

fun main() {
    val student = Student()
  	//Java中的泛型上界对应Kotlin的协变类型
    val data: MutableList<out Number> = student.data
  	//Java中的泛型下界对应Kotlin的抗变类型
    val data2: MutableList<in Int> = student.data2
}

由于在Kotlin中对in和out进行了严格的使用限制,因此无法像Java那样随意使用。

  • Java的原始类型被转换为星形投影:
    • List当做List<*>!也就是List<out Any?>!
public class Student {
    List data;   //对List的原始使用
}
fun main() {
    val student = Student()
  	//在Kotlin中直接以Any?作为实际类型使用,因为没有明确具体类型
    val data: MutableList<Any?>? = student.data
}

与Java一样,Kotlin的泛型不会在运行时保留,也就是说对象不会带有任何实际类型参数的信息,详情请见基础篇。

运算符重载

由于Java不支持运算符重载,我们无法通过关键字支持像Kotlin这样的运算符重载,但是,只要符合我们前面所说的那些运算符重载函数名称的方法,依然可以作为运算符重载函数使用:

public class Student {
    public Student plus(Student other) {
        return this;
    }
}

可以看到,以上代码与Kotlin中+运算符重载函数的定义相同,满足规范,因此,在Kotlin中,可以直接支持使用:

fun main() {
    var student = Student()
    student = student + student
}

异常检查

在Kotlin中,所有异常都不会主动进行检查,这意味着编译器不会强迫您捕获任何异常。因此,当您调用声明了异常的Java方法时,Kotlin不会强制要求进行捕获:

public class Student {
    public void test() throws IOException {
        
    }
}
fun main() {
    var student = Student()
    student.test()
}

Object类型

当Java对象在Kotlin中使用时,Object类型的所有引用都会变成Any类型。由于Any类型不是特定于某一个平台的,考虑到对其他语言的兼容性,因此,它只声明toString()``hashCode()equals()函数作为其成员,而在Java中,Object存在很多其他的成员方法:

public final native Class<?> getClass();
public final native void notify();
public final native void notifyAll();
...

如果需要让Object类中的其他成员方法可用,可以像下面这样:

fun main() {
    val student = Student()
  	//将student类型转换为Object再调用其
    (student as Object).wait()
}

不过,在Kotlin中不鼓励使用wait()notify()等线程相关方法,使用JUC中提供的类型效果更佳(详情请见Java JUC篇视频教程)

如果我们要获取某个类的Class对象:

fun main() {
    val student = Student()
    val clazz: Class<Student> = student.javaClass  //使用.javaClass获取到对应的Java类对象
}

函数式接口

Kotlin支持Java的SAM转换,只要是Java中满足要求的函数式接口,都可以开箱即用:

@FunctionalInterface
public interface Runnable {
    public abstract void run();
}
fun main() {
  	//使用Java中的Runnable接口
    val runnable: Runnable = Runnable {
        println("Hello World")
    }
}

包括我们在函数中一样可以像这样使用:

import java.util.concurrent.ThreadPoolExecutor

fun main() {
    val executor = ThreadPoolExecutor()
    // Java方法定义: void execute(Runnable command)
    executor.execute { println("This runs in a thread pool") }
}

Java调用Kotlin

前面我们介绍了Kotlin如何调用现成的Java代码,我们接着来看Java如何调用Kotlin代码。由于Java和Kotlin之间存在某些差异,在将Kotlin代码集成到Java中时需要注意很多东西。

对象属性

在Kotlin中定义的类型,到了Java中依然可以直接创建其对象:

class Student(var name: String)
public static void main(String[] args) {
    Student student = new Student("小明");   //Kt构造函数就是构造方法
}

只不过,对于类的属性,由于Kotlin中本质是以get和set函数的形式存在的,因此,我们只能使用对应的Getter和Setter方法来进行调用:

public static void main(String[] args) {
    Student student = new Student("小明");
    student.getName();
    student.setName("大明");   //set方法仅在属性为var时可用
}

如果实在是需要在Java中像使用普通变量那样,我们可以添加一个特殊的注解使其支持:

class Student(@JvmField var name: String)

包括懒加载属性也支持:

class Student {
    lateinit var name: String
}
public static void main(String[] args) {
    Student student = new Student("小明");
    student.name = "";
}

当然,这个属性不得是 openoverrideconst 其中一种,也不能是委托属性。

同时,由于Java中不存在空类型处理,因此,在Kotlin中定义的无论是否为可空类型都可以在Java中直接使用:

class Student {
    var name: String? = null
}
public static void main(String[] args) {
    Student student = new Student();
    student.getName().toUpperCase();   //直接空指针异常
}

静态属性

如果是Kotlin文件中直接编写的顶层定义,可以当做特定文件的静态属性来使用:

fun test() {
    println("Hello World")
}

在编译之后,它本质上就是一个Java中的静态方法,而对应类的名称就是源文件名称+Kt,这里Main.kt对应的名称就是MainKt了:

public static void main(String[] args) {
    MainKt.test();
}

我们也可以使用注解来明确生成的Java字节码文件名称:

@file:JvmName("LBWNB")
public static void main(String[] args) {
    LBWNB.test();
}

对于伴生对象以及单例对象,在Java中使用起来可能会有些别扭:

class Student {
    companion object {
        fun test() {}
    }
}

object Test {
    fun hello() {}
}
public static void main(String[] args) {
    Student.Companion.test();
  	Test.INSTANCE.hello();
}

这并不是我们希望的样子,在Kotlin中我们可以直接使用Student.的形式来直接调用,而在Java中却有一些出入,我们可以为这些函数添加@JvmStatic注解来完成:

class Student {
    companion object {
        @JvmStatic fun test() {}
    }
}

对于伴生对象中的字段,我们也可以为其添加@JvmField注解来使得其可以直接使用,这会在编译时使得此函数作为Student的静态属性存在:

public static void main(String[] args) {
    Student.test();
}

同样的,对于伴生对象中的属性来说,我们也像上一小节那样添加@JvmField注解:

class Student {
    companion object {
        @JvmField var name: String = ""
    }
}
public static void main(String[] args) {
    Student.name = "";   //此时name就是Student的静态属性
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1404240.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

RK3568 移植Ubuntu

使用ubuntu-base构建根文件系统 1、到ubuntu官网获取 ubuntu-base-18.04.5-base-arm64.tar.gz Ubuntu Base 18.04.5 LTS (Bionic Beaver) 2、将获取的文件拷贝到ubuntu虚拟机,新建目录,并解压 mkdir ubuntu_rootfs sudo tar -xpf u

Ubuntu之离线安装Gitlab,搭建私有代码仓库

Ubuntu之离线安装Gitlab,搭建私有代码仓库 文章目录 Ubuntu之离线安装Gitlab,搭建私有代码仓库1. 官网下载&#xff1a;2. 安装Gitlab3. 使用 1. 官网下载&#xff1a; https://packages.gitlab.com/gitlab/gitlab-ce wget下载地址&#xff1a; wget https://packages.gitla…

Linux命令_vim的详细用法

简介 vim是一款针对Linux和其他类Unix操作系统的文本编辑器。它是Vi编辑器的升级版本&#xff0c;具有丰富的功能和强大的扩展性。vim有三种基本模式&#xff1a;命令模式、插入模式和可视模式。 命令模式&#xff1a;用户可以使用各种命令移动光标和进行编辑操作&#xff0c;如…

安裝火狐和穀歌流覽器插件FoxyProxy管理海外動態IP代理

代理生態系統擁有大量有用的實用程式&#xff0c;使海外代理IP代理設置的使用變得簡單起來。其中一種類型叫做代理管理工具&#xff0c;像FoxyProxy就是該工具集比較受歡迎的。 本文將全面解析FoxyProxy擴展的功能和特性、Foxyproxy怎麼下載、以及如何在穀歌流覽器和火狐流覽器…

数据分析的理念、流程、方法、工具(上)

一、数据的价值 1、数据驱动企业运营 从电商平台的「猜你喜欢」到音乐平台的「心动模式」&#xff0c;大数据已经渗透到了我们生活的每一个场景。不论是互联网行业&#xff0c;还是零售业、制造业等&#xff0c;各行各业都在依托互联网大数据&#xff08;数据采集、数据存储、…

AutoDL——终端训练神经网络模型(忽略本地问题)

前言&#xff1a; 本人之前分享过一篇文章&#xff1a;使用pycharm连接远程GPU训练神经网络模型&#xff08;超详细&#xff01;&#xff09;&#xff0c;其中详细介绍了如何利用pycharm连接AutoDL算力云平台租用的GPU服务器训练网络模型。但有些小伙伴可能会因为一些原因而导…

清越 peropure·AI 国内版ChatGP新功能介绍

当OpenAI发布ChatGPT的时候,没有人会意识到,新一代人工智能浪潮将给人类社会带来一场眩晕式变革。其中以ChatGPT为代表的AIGC技术加速成为AI领域的热门发展方向,推动着AI时代的前行发展。面对技术浪潮,清越科技(PeroPure)立足多样化生活场景、精准把握用户实际需求,持续精确Fin…

【爬虫、数据可视化实战】以“人口”话题为例爬取实时微博数据并进行舆情分析

前言&#xff1a; 近期在weibo上讨论的比较热的话题无非就是“人口”了。TaoTao也看了一些大家发的内容。但是感觉单纯的看文字内容不能很直观的反应出来大家的关切。索性就使用爬虫对数据进行爬取&#xff0c;同时结合着数据可视化的方式让数据自己开口说话。那么接下来就让我…

浮点数详解

目录 1.概述 2.浮点数的编码方式 2.1.float类型的IEEE编码 2.2.double类型的IEEE编码 2.3.现场问题 2.4.总结 1.概述 计算机也需要运算和存储数学中的实数。在计算机的发展过程中&#xff0c;曾产生过多种存储实数的方式&#xff0c;有的现在已经很少使用了。不管如何存储…

OpenCV书签 #差值哈希算法的原理与相似图片搜索实验

1. 介绍 差值哈希算法&#xff08;Difference Hash Algorithm&#xff0c;简称dHash&#xff09; 是哈希算法的一种&#xff0c;主要可以用来做以图搜索/相似图片的搜索工作。 2. 原理 差值哈希算法通过计算相邻像素的差异来生成哈希&#xff0c;即通过缩小图像的每个像素与平…

macbookpro怎么恢复出厂设置2024最新恢复方法汇总

可能你的MacBook曾经是高性能的代表&#xff0c;但是现在它正慢慢地逝去了自己的光芒&#xff1f;随着逐年的使用以及文件的添加和程序的安装&#xff0c;你的MacBook可能会开始变得迟缓卡顿&#xff0c;或者失却了以往的光彩。如果你发现你的Mac开始出现这些严重问题&#xff…

c#中使用UTF-8编码处理多语言文本的有效策略

使用UTF-8编码处理多语言文本的有效策略 在当今的全球化时代&#xff0c;软件开发者常常需要处理包含多种语言的文本。这不仅涉及英文和其他西方语言&#xff0c;还包括中文、日文、韩文等多字节字符系统。在这篇博客中&#xff0c;我将探讨如何有效地使用UTF-8编码来处理混合语…

基于SpringBoot Vue二手闲置物品交易系统

大家好✌&#xff01;我是Dwzun。很高兴你能来阅读我&#xff0c;我会陆续更新Java后端、前端、数据库、项目案例等相关知识点总结&#xff0c;还为大家分享优质的实战项目&#xff0c;本人在Java项目开发领域有多年的经验&#xff0c;陆续会更新更多优质的Java实战项目&#x…

unity shaderGraph实例-武器特效(纹理遮罩,纹理动画,纹理变形)

文章目录 效果展示所需素材整体结构各区域内容区域1区域2区域3区域4区域4-1区域4-2区域4-3区域4-4 区域5区域6 后处理工程下载 效果展示 所需素材 除了剑的模型外&#xff0c;主要是这五张贴图&#xff0c;其中swordmask和swordmask1中白色的区域是剑身的位置&#xff0c;sword…

Visual Studio2022实用使用技巧集

前言 对于.NET开发者而言Visual Studio是我们日常工作中比较常用的开发工具&#xff0c;掌握一些Visual Studio实用的搜索、查找、替换技巧可以帮助我们大大提高工作效率从而避免996。 Visual Studio更多实用技巧 https://github.com/YSGStudyHards/DotNetGuide 代码和功能搜…

上门回收小程序,打造回收新模式

近年来&#xff0c;我国一直秉持着环保绿色的发展理念&#xff0c;为了减少资源浪费&#xff0c;旧物回收成为了人们处理废弃物品的方式。目前&#xff0c;我国回收市场规模大约能达到3.58亿元&#xff0c;在我国经济的稳定增长和环保意识的提高下&#xff0c;回收市场规模还将…

【Java】--网络编程:基于TCP协议的网络通信

【Java】–网络编程&#xff1a;基于TCP协议的网络通信 文章目录 【Java】--网络编程&#xff1a;基于TCP协议的网络通信一、TCP协议1.1 概念1.2 三次握手1.2.1 文字描述1.2.2 画图演示 1.3 四次挥手1.3.1 文字描述1.3.2 画图演示 二、基于TCP的Socket网络编程2.1 概念2.2 服务…

Android 通过adb命令查看应用流量

一. 获取应用pid号 通过adb shell ps -A | grep 包名 来获取app的 pid号 二. 查看应用流量情况 使用adb shell cat /proc/#pid#/net/dev 命令 来获取流量数据 备注&#xff1a; Recevice: 表示收包 Transmit: 表示发包 bytes: 表示收发的字节数 packets: 表示收发正确的…

thinkphp+vue+mysql旅游推荐攻略分享网站p0667

基于php语言设计并实现了旅游分享网站。该系统基于B/S即所谓浏览器/服务器模式&#xff0c;应用thinkphp框架&#xff0c;选择MySQL作为后台数据库。系统主要包括用户、景点信息、攻略分类、旅游攻略、门票购买、留言反馈、论坛管理、系统管理等功能模块。运行环境:phpstudy/wa…

实现纯Web语音视频聊天和桌面分享(附源码,PC端+移动端)

在网页里实现文字聊天是比较容易的&#xff0c;但若要实现视频聊天&#xff0c;就比较麻烦了。本文将实现一个纯Web版的视频聊天和桌面分享的Demo&#xff0c;可直接在浏览器中运行&#xff0c;不需要安装任何插件。 一. 主要功能及支持平台 1.本Demo的主要功能有 &#xff…