文章汇总

发布于2024-06-16·10

「笔记」Java:数组

数组01:数组概述

关于数组我们可以把它看作是一个类型的所有数据的一个集合,并用一个数组下标来区分或指定每一个数,例如一个足球队通常会有几十个人,但是我们来认识他们的时候首先会把他们看作是某某对的成员,然后再利用他们的号码来区分每一个队员,这时候,球队就是一个数组,而号码就是数组的下标,当我们指明是几号队员的时候就找到了这个队员。 同样在编程中,如果我们有一组相同数据类型的数据,例如有10个数字,这时候如果我们要用变量来存放它们的话,就要分别使用10个变量,而且要记住这10个变量的名字,这会十分的麻烦,这时候我们就可以用一个数组变量来存放他们,例如在VB中我们就可以使用dim a(9) as integer(注意:数组的下标是从0开始的,所以10个数的话,下标就是9,a(0)=1)。 使用数组会让程序变的简单,而且避免了定义多个变量的麻烦。 **数组的定义:** - 数组是相同类型数据的有序集合。 - 数组描述的是相同类型的若干个数据,按照一定的先后次序排列组合而成。 - 其中,每一个数据称作一个数组元素,每个数组元素可以通过一个下标来访问它们。 **数组的四个基本特点:** 1. 其长度是确定的。数组一旦被创建,它的大小就是不可以改变的。 2. 其元素必须是相同类型,不允许出现混合类型。 3. 数组中的元素可以是任何数据类型,包括基本类型和引用类型。 4. 数组变量属引用类型,数组也可以看成是对象,数组中的每个元素相当于该对象的成员变量。数组本身就是对象,Java中对象是在堆中的,因此数组无论保存原始类型还是其他对象类型,**数组对象本身是在堆中的**。

发布于2024-06-16·5

「笔记」Java:数组

数组02:数组声明创建

### 1. 声明数组 首先必须声明数组变量,才能在程序中使用数组。下面是声明数组变量的语法: ```java dataType[] arrayRefVar; // 首选的方法 或 dataType arrayRefVar[]; // 效果相同,但不是首选方法 ``` 建议使用 **dataType[] arrayRefVar** 的声明风格声明数组变量。 dataType arrayRefVar[] 风格是来自C/C++ 语言 ,在Java中采用是为了让 C/C++ 程序员能够快速理解java语言。 ```java double[] myList; // 首选的方法 或 double myList[]; // 效果相同,但不是首选方法 ``` ------------ ### 2. 创建数组 Java语言使用new操作符来创建数组,语法如下: ```java arrayRefVar = new dataType[arraySize]; ``` 上面的语法语句做了两件事: 1. 使用 dataType[arraySize] 创建了一个数组。 2. 把新创建的数组的引用赋值给变量 arrayRefVar。 数组变量的声明,和创建数组可以用一条语句完成,如下所示: ```java dataType[] arrayRefVar = new dataType[arraySize]; ``` 数组的元素是通过索引访问的。数组索引从 0 开始,所以索引值从 0 到 arrayRefVar.length-1。 获取数组长度: ```java arrays.length ``` 【演示创建一个数组,并赋值,进行访问】 ```java public static void main(String[] args) { //1.声明一个数组 int[] myList = null; //2.创建一个数组 myList = new int[10]; //3.像数组中存值 myList[0] = 1; myList[1] = 2; myList[2] = 3; myList[3] = 4; myList[4] = 5; myList[5] = 6; myList[6] = 7; myList[7] = 8; myList[8] = 9; myList[9] = 10; // 计算所有元素的总和 double total = 0; for (int i = 0; i < myList.length; i++) { total += myList[i]; } System.out.println("总和为: " + total); } ``` ------------ ### 3. 内存分析 ![image](https://img2024.cnblogs.com/blog/2420696/202408/2420696-20240813213337731-1954934238.png) 1. 声明的时候并没有实例化任何对象,只有在实例化数组对象时,JVM才分配空间,这时才与长度有关。因此,声明数组时不能指定其长度(数组中元素的个数),例如: int a[5]; //非法 2. 声明一个数组的时候并没有数组被真正的创建。 3. 构造一个数组,必须指定长度。 ```java // 1.声明一个数组 int[] myList = null; ``` ![image](https://img2024.cnblogs.com/blog/2420696/202408/2420696-20240813213513859-705486741.png) ```java // 2.创建一个数组 myList = new int[10]; ``` ![image](https://img2024.cnblogs.com/blog/2420696/202408/2420696-20240813213540274-419772039.png) ```java // 3.向数组中存值 myList[0] = 1; myList[1] = 2; myList[2] = 3; myList[3] = 4; myList[4] = 5; myList[5] = 6; myList[6] = 7; myList[7] = 8; myList[8] = 9; myList[9] = 10; ``` ![image](https://img2024.cnblogs.com/blog/2420696/202408/2420696-20240813213625003-922416156.png) ------------ ### 4. 三种初始化 **静态初始化** 除了用new关键字来产生数组以外,还可以直接在定义数组的同时就为数组元素分配空间并赋值。 ```java int[] a = {1,2,3}; Man[] mans = {new Man(1,1),new Man(2,2)}; ``` **动态初始化** 数组定义、为数组元素分配空间、赋值的操作、分开进行。 ```java int[] a = new int[2]; a[0]=1; a[1]=2; ``` **数组的默认初始化** 数组是引用类型,它的元素相当于类的实例变量,因此数组一经分配空间,其中的每个元素也被按照实 例变量同样的方式被隐式初始化。 ```java public static void main(String[] args) { int[] a=new int[2]; boolean[] b = new boolean[2]; String[] s = new String[2]; System.out.println(a[0]+":"+a[1]); //0,0 System.out.println(b[0]+":"+b[1]); //false,false System.out.println(s[0]+":"+s[1]); //null, null } ``` ------------ ### 5. 数组边界 下标的合法区间:[0, length-1],如果越界就会报错; ```java public static void main(String[] args) { int[] a = new int[2]; System.out.println(a[2]); // 越界了,应为0-1 } ``` **ArrayIndexOutOfBoundsException : 数组下标越界异常!** ------------ ### 6. 小结 数组是相同数据类型(数据类型可以为任意类型)的有序集合。 数组也是对象。数组元素相当于对象的成员变量(详情请见内存图)。 数组长度的确定的,不可变的。如果越界,则报:ArrayIndexOutofBounds。

发布于2024-08-18·6

「笔记」Java:数组

数组03:数组使用

数组的元素类型和数组的大小都是确定的,所以当处理数组元素时候,我们通常使用基本循环或者 ForEach 循环。 【该实例完整地展示了如何创建、初始化和操纵数组】 ```java public class TestArray { public static void main(String[] args) { double[] myList = {1.9, 2.9, 3.4, 3.5}; // 打印所有数组元素 for (int i = 0; i < myList.length; i++) { System.out.println(myList[i] + " "); } // 计算所有元素的总和 double total = 0; for (int i = 0; i < myList.length; i++) { total += myList[i]; } System.out.println("Total is " + total); // 查找最大元素 double max = myList[0]; for (int i = 1; i < myList.length; i++) { if (myList[i] > max) { max = myList[i]; } } System.out.println("Max is " + max); } } ``` ### 1. For-Each 循环 JDK 1.5 引进了一种新的循环类型,被称为 For-Each 循环或者加强型循环,它能在不使用下标的情况下遍历数组。 语法格式如下: ```java for(type element: array){ System.out.println(element); } ``` 【示例】 ```java public static void main(String[] args) { double[] myList = {1.9, 2.9, 3.4, 3.5}; // 打印所有数组元素 for (double element: myList) { System.out.println(element); } } ``` ------------ ### 2. 数组作方法入参 数组可以作为参数传递给方法。 例如,下面的例子就是一个打印 int 数组中元素的方法: ```java public static void printArray(int[] array) { for (int i = 0; i < array.length; i++) { System.out.print(array[i] + " "); } } ``` ------------ ### 3. 数组作返回值 ```java public static int[] reverse(int[] list) { int[] result = new int[list.length]; for (int i = 0, j = result.length - 1; i < list.length; i++, j--) { result[j] = list[i]; } return result; } ``` 以上实例中 result 数组作为函数的返回值。

发布于2024-08-18·1

「笔记」Java:数组

数组04:多维数组

多维数组可以看成是数组的数组,比如二维数组就是一个特殊的一维数组,其每一个元素都是一个一维数组。 **多维数组的动态初始化(以二维数组为例)** 直接为每一维分配空间,格式如下: ```java type[][] typeName = new type[typeLength1][typeLength2]; ``` type 可以为基本数据类型和复合数据类型,arraylenght1 和 arraylenght2 必须为正整数,arraylenght1 为行数,arraylenght2 为列数。 比如定义一个二维数组: ```java int a[][] = new int[2][5]; ``` 解析:二维数组 a 可以看成一个两行三列的数组。 **多维数组的引用(以二维数组为例)** 对二维数组中的每个元素,引用方式为 **arrayName[index1] [index2]**,例如: num[1] [0]; 其实二维甚至多维数组十分好理解,我们把两个或者多个值当做定位就好。 原来的数组就是一条线,我们知道一个位置就好 二维就是一个面,两点确定一个位置 三维呢,就需要三个点来确定 依次理解即可! **获取数组长度:** a.length获取的二维数组第一维数组的长度,a[0].length才是获取第二维第一个数组长度。

发布于2024-08-18·4

「笔记」Java:数组

数组05:Arrays 类

数组的工具类java.util.Arrays 由于数组对象本身并没有什么方法可以供我们调用,但API中提供了一个工具类Arrays供我们使用,从而可以对数据对象进行一些基本的操作。 **文档简介:** 此类包含用来操作数组(比如排序和搜索)的各种方法。此类还包含一个允许将数组作为列表来查看的静态工厂。除非特别注明,否则如果指定数组引用为nul1,则此类中的方法都会抛出NullPointerException。 Arrays类中的方法都是static修饰的静态方法,在使用的时候可以直接使用类名进行调用,而"不用"使用对象来调用(注意:是"不用" 而不是 "不能") java.util.Arrays 类能方便地操作数组. 使用之前需要导包! 具有以下常用功能: - 给数组赋值:通过 fill 方法。 - 对数组排序:通过 sort 方法,按升序。 - 比较数组:通过 equals 方法比较数组中元素值是否相等。 - 查找数组元素:通过 binarySearch 方法能对排序好的数组进行二分查找法操作。 具体说明请查看下表: ![image](https://img2024.cnblogs.com/blog/2420696/202408/2420696-20240813221758386-1936055772.png) ### 1. 打印数组 ```java public static void main(String[] args) { int[] a = {1,2}; System.out.println(a); //[I@1b6d3586 System.out.println(Arrays.toString(a)); //[1, 2] } ``` ------------ ### 2. 数组排序 对指定的 int 型数组按数字升序进行排序 ```java public static void main(String[] args) { int[] a = {1,2,323,23,543,12,59}; System.out.println(Arrays.toString(a)); Arrays.sort(a); System.out.println(Arrays.toString(a)); } ``` ------------ ### 3. 二分法查找 在数组中查找指定元素并返回其下标 注意:使用二分搜索法来搜索指定的数组,以获得指定的值。必须在进行此调用之前对数组进行排序(通过sort方法等)。如果没有对数组进行排序,则结果是不确定的。 如果数组包含多个带有指定值的元素,则无法保证找到的是哪一个。 ```java public static void main(String[] args) { int[] a = {1,2,323,23,543,12,59}; Arrays.sort(a); // 使用二分法查找,必须先对数组进行排序 System.out.println("该元素的索引:"+Arrays.binarySearch(a, 12)); } ``` ------------ ### 4. 元素填充 ```java public static void main(String[] args) { int[] a = {1,2,323,23,543,12,59}; Arrays.sort(a); // 使用二分法查找,必须先对数组进行排序 Arrays.fill(a, 2, 4, 100); // 将2到4索引的元素替换为100 System.out.println(Arrays.toString(a)); } ``` ------------ ### 5. 数组转换为List集合 ```java int[] a = {3,5,1,9,7}; List<int[]> list = Arrays.asList(a); ```

发布于2024-11-18·8

「笔记」Java:数组

数组06:常见排序算法

### 1. 冒泡排序 冒泡排序(Bubble Sort),是一种计算机科学领域的较简单的排序算法。 它重复地走访过要排序的元素列,依次比较两个相邻的元素,如果他们的顺序(如从大到小、首字母从A到Z)错误就把他们交换过来。走访元素的工作是重复地进行直到没有相邻元素需要交换,也就是说该元素列已经排序完成。 这个算法的名字由来是因为越大的元素会经由交换慢慢“浮”到数列的顶端(升序或降序排列),就如同碳酸饮料中二氧化碳的气泡最终会上浮到顶端一样,故名“冒泡排序”。 冒泡排序算法的原理如下: 1. 比较相邻的元素。如果第一个比第二个大,就交换他们两个。 2. 对每一对相邻元素做同样的工作,从开始第一对到结尾的最后一对。在这一点,最后的元素应该会是最大的数。 3. 针对所有的元素重复以上的步骤,除了最后一个。 4. 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。 ```java class Bubble { public int[] sort(int[] array) { int temp = 0; // 外层循环,它决定一共走几趟 // -1为了防止溢出 for (int i = 0; i < array.length - 1; i++) { int flag = 0; // 通过符号位可以减少无谓的比较,如果已经有序了,就退出循环 // 内层循环,它决定每趟走一次 for (int j = 0; j < array.length - i - 1; j++) { // 如果后一个大于前一个,则换位 if (array[j + 1] > array[j]) { temp = array[j]; array[j] = array[j + 1]; array[j + 1] = temp; flag = 1; } } if (flag == 0) { break; } } return array; } public static void main(String[] args) { Bubble bubble = new Bubble(); int[] array = {2, 5, 1, 6, 4, 9, 8, 5, 3, 1, 2, 0}; int[] sort = bubble.sort(array); for (int num : sort) { System.out.print(num + "\t"); } } } ``` ------------ ### 2. 选择排序 选择排序(Selection sort)是一种简单直观的排序算法。它的工作原理是每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到排序序列的末尾。以此类推,直到全部待排序的数据元素排完。 选择排序是不稳定的排序方法。 ```java class SelectSort{ public int[] sort(int arr[]) { int temp = 0; for (int i = 0; i < arr.length - 1; i++) { // 认为目前的数就是最小的, 记录最小数的下标 int minIndex = i; for (int j = i + 1; j < arr.length; j++) { if (arr[minIndex] > arr[j]) { // 修改最小值的下标 minIndex = j; } } // 当退出for就找到这次的最小值,就需要交换位置了 if (i != minIndex) { //交换当前值和找到的最小值的位置 temp = arr[i]; arr[i] = arr[minIndex]; arr[minIndex] = temp; } } return arr; } public static void main(String[] args) { SelectSort selectSort = new SelectSort(); int[] array = {2, 5, 1, 6, 4, 9, 8, 5, 3, 1, 2, 0}; int[] sort = selectSort.sort(array); for (int num : sort) { System.out.print(num + "\t"); } } } ```

发布于2024-11-18·244

「笔记」Java:数组

数组07:稀疏数组

### 1. 线性结构 - 线性结构是最常用的数据结构,其特点是数据元素之间存在一对一的线性关系。 - 线性结构有两种不同存储结构,即顺序存储结构和链式存储结构。 - 顺序存储的线性表称为顺序表,顺序表中的存储元素是连续的,即在内存中是连续的,例如数组。 - 链式存储的线性表称为链表,链表中的存储元素不一定是连续的,元素节点中存放数据元素以及相邻元素的地址信息,但好处是可以充分利用碎片地址。 - 线性结构常见的有:数组、队列、链表和栈,后面我们会详细讲解。 ------------ ### 2. 非线性结构 非线性结构不是一对一的关系, 非线性结构包括:二维数组,多维数组,广义表,树结构,图结构 ------------ ### 3. 稀疏数组 先看一个需求,在编写五子棋程序时,有存盘退出和续上盘的功能。 ![image](https://img2024.cnblogs.com/blog/2420696/202408/2420696-20240813223847054-535002083.png) 因为该二维数组的很多值是默认值 0, 因此记录了很多没有意义的数据,因此,在这里,我们需要引入一个新的概念——稀疏数组 当一个数组中大部分元素为0,或者为同一个值的数组时,可以使用稀疏数组来保存该数组,稀疏数组的处理方法是: - 记录数组一共有几行几列,有多少个不同的值 - 把具有不同值的元素的行列及值记录在一个小规模的数组中,从而缩小程序的规模 ![image](https://img2024.cnblogs.com/blog/2420696/202408/2420696-20240813223915961-583112668.png) 即第一行第一列记录记录原始数组行数,第一行第二列记录原始数组列数,第一行第三列总共有多少个值,第二行记录第一个有效数据,第三行记录第二个有效数据 代码实现 ```java public class SparseArr { public static void main(String[] args) { //创建一个二维数组 11*11 //0表示没有棋子,1表示黑棋,2表示蓝棋 int[][] chessArr = new int[11][11]; chessArr[1][2] = 1; chessArr[2][3] = 2; //输出原始的二维数组 System.out.println("原始的二维数组:"); for (int i = 0; i < chessArr.length; i++) { for (int j = 0; j < chessArr[i].length; j++) { System.out.print(chessArr[i][j]+"\t"); } System.out.println(); } //将二维数组转换为稀疏数组 //1.先遍历二维数组得到非零数据的个数 int sum = 0; for (int i = 0; i < 11; i++) { for (int j = 0; j < 11; j++) { if (chessArr[i][j] != 0){ sum++; } } } //2.创建对应的系数数组 int[][] sparseArr = new int[sum+1][3]; //给系数数组赋值 sparseArr[0][0] = 11; sparseArr[0][1] = 11; sparseArr[0][2] = sum; //遍历二维数组将非零的值存放到稀疏数组 int count = 0; for (int i = 0; i < chessArr.length; i++) { for (int j = 0; j < chessArr[i].length; j++) { if (chessArr[i][j] != 0){ count++; sparseArr[count][0] = i; sparseArr[count][1] = j; sparseArr[count][2] = chessArr[i][j]; } } } //输出稀疏数组 System.out.println(); System.out.println("稀疏数组:"); for (int i = 0; i < sparseArr.length; i++) { System.out.println(sparseArr[i][0]+"\t"+sparseArr[i][1]+"\t"+sparseArr[i][2]); } //将稀疏数组恢复成二维数组 //1.先读取稀疏数组的第一行,根据第一行创建二维数组 int[][] chessArr2 = new int[sparseArr[0][0]][sparseArr[0][1]]; //2.读取稀疏数组后几行赋值给二维数组 //注意这里是从第二行开始 for (int i = 1; i < sparseArr.length; i++) { chessArr2[sparseArr[i][0]][sparseArr[i][1]] = sparseArr[i][2]; } System.out.println(); System.out.println("恢复后的二维数组:"); for (int[] row : chessArr2) { for (int data : row) { System.out.print(data+"\t"); } System.out.println(); } } } ``` ------------

发布于2024-11-30·48

「笔记」Java:方法

方法06:递归

A方法调用B方法,我们很容易理解! 递归就是:A方法调用A方法!就是自己调用自己,因此我们在设计递归算法时,一定要指明什么时候自己不调用自己。否则,就是个死循环! ### **递归算法重点** 递归是一种常见的解决问题的方法,即把问题逐渐简单化。递归的基本思想就是“自己调用自己”,一个使用递归技术的方法将会直接或者间接的调用自己。 利用递归可以用简单的程序来解决一些复杂的问题。它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解,递归策略只需少量的程序就可描述出解题过程所需要的多次重复计算,大大地减少了程序的代码量。递归的能力在于用有限的语句来定义对象的无限集合。 递归结构包括两个部分: 1. 递归头。解释:什么时候不调用自身方法。如果没有头,将陷入死循环。 2. 递归体。解释:什么时候需要调用自身方法。 【演示:利用代码计算5的乘阶!】 ```java // 5*4*3*2*1 public static void main(String[] args) { System.out.println(f(5)); } public static int f(int n) { if (1 == n) { return 1; } else { return n*f(n-1); } } ``` ![2e958347-5ad0-4680-bf64-d5dd7dc6f181.png](https://owoyu.fun/images/2024/11/30/2e958347-5ad0-4680-bf64-d5dd7dc6f181.png) 此题中,按照递归的三个条件来分析: 1. 边界条件:阶乘,乘到最后一个数,即1的时候,返回1,程序执行到底; 2. 递归前进段:当前的参数不等于1的时候,继续调用自身; 3. 递归返回段:从最大的数开始乘,如果当前参数是5,那么就是5 4,即5 (5-1),即n \* (n-1) 递归其实是方便了程序员难为了机器,递归可以通过数学公式很方便的转换为程序。其优点就是易理解,容易编程。但递归是用栈机制实现的,每深入一层,都要占去一块栈数据区域,对嵌套层数深的一些算法,递归会力不从心,空间上会以内存崩溃而告终,而且递归也带来了大量的函数调用,这也有许多额外的时间开销。所以在深度大时,它的时空性就不好了。(会占用大量的内存空间) 而迭代虽然效率高,运行时间只因循环次数增加而增加,没什么额外开销,空间上也没有什么增加,但缺点就是不容易理解,编写复杂问题时困难。 **能不用递归就不用递归,递归都可以用迭代来代替。**

发布于2025-01-04·4

StatelessWidget与StatefulWidget开发指南

Flutter的widget很多,但主要可以分为两类,一种是无状态的widget(StatelessWidget),一种是有状态的widget(StatefulWidget)。

## 什么是StatelessWidget? Flutter中的`StatelessWidget`是一个不需要状态更改的widget - 它没有要管理的内部状态。 当您描述的用户界面部分不依赖于对象本身中的配置信息以及widget的BuildContext 时,无状态widget非常有用。 `AboutDialog`, `CircleAvatar`和 `Text` 都是`StatelessWidget`的子类。 ```dart void main() => runApp(const MyStatelessWidget(text: "StatelessWidget Example")); class MyStatelessWidget extends StatelessWidget { final String text; const MyStatelessWidget({Key? key, required this.text}) : super(key: key); @override Widget build(BuildContext context) { return Center( child: Text( text, textDirection: TextDirection.ltr, ), ); } } ``` 在前面的示例中,您使用了MyStatelessWidget类的构造函数 传递标记为final的text。这个类继承了StatelessWidget-它包含不可变数据 无状态widget的build方法通常只会在以下三种情况调用: * 将widget插入树中时 * 当widget的父级更改其配置时 * 当它依赖的[InheritedWidget](https://api.flutter.dev/flutter/widgets/InheritedWidget-class.html)发生变化时 --- ## 什么是StatefulWidget? [StatefulWidget](https://api.flutter.dev/flutter/widgets/StatefulWidget-class.html) 是可变状态的widget。 使用`setState`方法管理StatefulWidget的状态的改变。调用`setState`告诉Flutter框架,某个状态发生了变化,Flutter会重新运行build方法,以便应用程序可以应用最新状态。 状态是在构建widget时可以同步读取的信息可能会在widget的生命周期中发生变化。确保在状态改变时及时通知状态 变化是widget实现者的责任。当widget可以动态更改时,需要使用StatefulWidget。 例如, 通过键入表单或移动滑块来更改widget的状态. 或者, 它可以随时间变化 - 或者数据推送更新UI `Checkbox`, `Radio`, `Slider`, `InkWell`, `Form`, 和 `TextField` 都是有状态的widget,也是StatefulWidget的子类。 下面的示例声明了一个StatefulWidget,它需要一个`createState()`方法。此方法创建管理widget状态的状态对象\_MyStatefulWidgetState。 ```dart class MyStatefulWidget extends StatefulWidget { MyStatefulWidget({Key key, this.title}) : super(key: key); final String title; @override _MyStatefulWidgetState createState() => _MyStatefulWidgetState(); } ``` 以下状态类\_MyStatefulWidgetState实现widget的`build()`方法。当状态改变时,例如,当用户切换按钮时,使用新的切换值调用`setState`。这会导致框架在UI中重建此widget。 ```dart import 'package:flutter/material.dart'; void main() => runApp(const MyStatefulWidget(title: "StatelessWidget与StatefulWidget开发指南")); class MyStatefulWidget extends StatefulWidget { const MyStatefulWidget({Key? key, required this.title}) : super(key: key); final String title; @override MyStatefulWidgetState createState() => MyStatefulWidgetState(); } class MyStatefulWidgetState extends State<MyStatefulWidget> { bool isOpen = false; @override Widget build(BuildContext context) { return MaterialApp( theme: ThemeData( useMaterial3: false, ), home: Scaffold( appBar: AppBar( title: Text(widget.title), ), body: Column( children: [ Text("开关状态:${isOpen ? '开启' : '关闭'}"), Switch( value: isOpen, onChanged: (change) { setState(() { isOpen = change; }); }) ], ), ), ); } } ``` --- ## StatefulWidget和StatelessWidget有哪些最佳实践? 在开发widget时,需要考虑以下几点。 > 1. 确定widget应该使用StatefulWidget还是StatelessWidget **在Flutter中,widget是有状态的还是无状态的 - 取决于是否 他们依赖于状态的变化** * 如果用户交互或数据改变导致widget改变,那么它就是有状态的。 * 如果一个widget是最终的或不可变的,那么它就是无状态。 > 2. 确定哪个对象管理widget的状态(对于StatefulWidget) 在Flutter中,管理状态有三种主要方式: * 每个widget管理自己的状态 * 父widget管理widget的状态 * 混合搭配管理的方法 如何决定使用哪种方式时,可以参考以下原则: * 如果所讨论的状态是用户数据,例如复选框的已选中或未选中状态,或滑块的位置,则状态最好由父widget管理; * 如果widget的状态取决于动作,例如动画,那么最好是由widget自身来管理状态; * 如有还是不确定谁管理状态,请让父widget管理子widget的状态;

发布于2024-11-29·4

JSON解析与Dart Model的使用

在Flutter中处理JSON数据并转换为Dart Model

### 将json string转成Dart Model类 通过上述方式可以将json字符串转换成Map,但Map中存放那些字段在使用的时候很不方便。 为了提高字段获取的效率,目前常用的方案是将json string转成Dart Model类,不仅可以提高字段获取的效率,而且方便字段的后期维护和扩展。 > 小知识:什么是Dart Model类? 实体类可以理解为一种数据的载体,主要是作为数据管理和业务逻辑处理层面上存在的类别。比如:我们将从数据库中或服务端接口中读取到的用户信息转成一个User类来存储;然后在需要的时候获取User类的属性并将其展示在界面上等。 ## 如何将json string转成Dart Model 1. 定义Dart Model 2. 将json string转成Map(可借助jsonDecode完成) 3. 将Map转成Dart Model 在定义Dart Model前我们先来了解下Dart Model的格式要求: ### Dart Model格式要求 1. 字段不能为私有(既字段前不能有下划线); 2. 普通构造函数; 3. 声明为`XXX.fromJson`的命名构造函数; 4. 声明为`Map<String, dynamic> toJson`成员函数; 其中:命名构造函数为必选。 > 代码示例: ```dart class Data { int? code; String? method; String? requestPrams; Data({this.code, this.method, this.requestPrams}); Data.fromJson(Map<String, dynamic> json) { code = json['code']; method = json['method']; requestPrams = json['requestPrams']; } Map<String, dynamic> toJson() { final Map<String, dynamic> data = <String, dynamic>{}; data['code'] = code; data['method'] = method; data['requestPrams'] = requestPrams; return data; } } ``` ## Dart Model的使用 定义好Dart Model之后接下来我们来看下如何使用它: 1. 使用`dart:convert` 中的`jsonDecode`将json string转成Map 2. 通过`DataModel.fromJson`将Map转成Dart Model > 代码示例: ```dart //json转Model void _json2Model() { var jsonString = '{"code":0,"data":{"code":0,"method":"GET","requestPrams":"11"},"msg":"SUCCESS."}'; Map<String, dynamic> map = jsonDecode(jsonString); //将json转成Map; DataModel model = DataModel.fromJson(map); //将Map转成Dart Model setState(() { resultShow = 'code: ${model.code};requestPrams:${model.data?.requestPrams}'; //使用Map中的数据 }); } ```

发布于2024-11-30·16

在Flutter中进行网络请求

Flutter官方推荐在Flutter中用Http进行网络请求

> Flutter官方推荐我们在Flutter中用Http进行网络请求。 ## 什么是Http库? Http库是Flutter社区开发的一个可组合的、跨平台的用于Flutter的网络请求插件。 ## 如何使用Http库 ### 第一步:添加依赖 在`pubspec.yaml`中引入[http](https://pub.dev/packages/http)插件; ```dart dependencies: http: <latest_version> ``` ### 第二步:导入http 在dart文件中导入: ```dart import 'package:http/http.dart' as http; ``` ## 使用http库做get请求 调用`http.get`发送请求: > 代码示例: ```dart ///发送Get请求 _doGet() async { var uri = Uri.parse('https://api.geekailab.com/uapi/test/test?requestPrams=11'); var response = await http.get(uri); //http请求成功 if (response.statusCode == 200) { setState(() { resultShow = response.body; }); } else { setState(() { resultShow = "请求失败:code: ${response.statusCode},body:${response.body}"; }); } } ``` `http.get()`返回一个包含`http.Response`的`Future`: * [Future](https://api.flutter.dev/flutter/dart-async/Future-class.html):是与异步操作一起工作的核心Dart类,它用于表示未来某个时间可能会出现的可用值或错误; * `http.Response`:类包含一个成功的HTTP请求接收到的数据; * `response.statusCode`:通过statusCode可以获取http请求状态码,200代表请求成功; * `response.body`:通过body获取返回的数据; ## 如何用http库做post请求? * 调用`http.post`发送请求; 使用post传递的数据常用的内容类型主要有两种: * `x-www-form-urlencoded` * `application/json` ### 发送`x-www-form-urlencoded`(后面简称form类型)类型的数据: form类型是post请求中较为常见的一种内容类型,也是http库默认的内容类型。 注意:body必须为`Map<String, String>`类型。 > 代码示例: ```dart _doPost() async { var uri = Uri.parse('https://api.geekailab.com/uapi/test/test'); var params = {"requestPrams": "doPost"};//数据格式必须为Map<String, String> var response = await http.post(uri, body: params); //默认为x-www-form-urlencoded 格式,所以可以不用设置content-type //http请求成功 if (response.statusCode == 200) { setState(() { resultShow = response.body; }); } else { setState(() { resultShow = "请求失败:code: ${response.statusCode},body:${response.body}"; }); } } ``` `http.post()`返回一个包含`http.Response`的`Future`: * [Future](https://api.flutter.dev/flutter/dart-async/Future-class.html):是与异步操作一起工作的核心Dart类。它用于表示未来某个时间可能会出现的可用值或错误; * `http.Response`:类包含一个成功的HTTP请求接收到的数据; ### 发送`application/json`(后面简称json类型)类型的数据 要发送json类型的数据,主要有以下步骤: 1. 将数据转换为json String,可以利用jsonEncode()来转; 2. 将json数据赋值给body参数; 3. 在header中设置`content-type`为`application/json`; ```dart ///发送Json类型的Post请求 _doPostJson() async { var uri = Uri.parse('https://api.geekailab.com/uapi/test/testJson'); var params = {"requestPrams": "doPost"}; var response = await http.post(uri, body: jsonEncode(params), //将数据转成string headers: { //设置content-type为application/json "content-type": "application/json" }); //http请求成功 if (response.statusCode == 200) { setState(() { resultShow = response.body; }); } else { setState(() { resultShow = "请求失败:code: ${response.statusCode},body:${response.body}"; }); } } ```