Java-02对象传递和返回

当你在“传递”一个对象的时候,你实际上是在传递它的引用

1引用

1.1传递引用

当你将一个引用传给方法后,该引用指向的仍然是原来的对象:

/**
* @Author Coder_Pans
* @Date 2022/11/20 10:14
* @PackageName:org.example.onjava.senior.example02ref
* @ClassName: PassRefences
* @Description: TODO 传递引用
* @Version 1.0
*/
public class PassRefences {
/**
* 当你将一个引用传给方法后,该引用指向的仍然是原来的对象
*/
public static void f(PassRefences h){
System.out.println("h inside f(): " + h);
} public static void main(String[] args) {
PassRefences p = new PassRefences();
System.out.println("p inside main(): " + p);
f(p);
}
}

1.2引用别名

引用别名指的是不止一个引用被绑定到了同一个对象上的情况。

别名导致的问题主要发生在有人对对象进行操作时。如果该对象的其他引用的持有者并不希望对象发生变更,那么结果将使其大感意外。

/**
* @Author Coder_Pans
* @Date 2022/11/20 10:18
* @PackageName:org.example.onjava.senior.example02ref
* @ClassName: Alias1
* @Description: TODO 引用别名
* @Version 1.0
*/
public class Alias1 {
private int i; public Alias1(int ii) {
this.i = ii;
} public static void main(String[] args) {
Alias1 x = new Alias1(7);
Alias1 y = x;
System.out.println("x.i = " + x.i);
System.out.println("y.i = " + y.i);
System.out.println("Incrementing x");
x.i++;
System.out.println("x.i = " + x.i);
System.out.println("y.i = " + y.i);
}
}
/**
* put:
* x.i = 7
* y.i = 7
* Incrementing x
* x.i = 8
* y.i = 8
*/

为了避免上面的情况,解决方案如下:

/**
* @Author Coder_Pans
* @Date 2022/11/20 10:18
* @PackageName:org.example.onjava.senior.example02ref
* @ClassName: Alias1
* @Description: TODO 引用别名,解决方案
* @Version 1.0
*/
public class Alias2 {
private int i; public Alias2(int ii) {
this.i = ii;
} public static void f(Alias2 ref){
ref.i++;
} public static void main(String[] args) {
Alias2 x = new Alias2(7);
Alias2 y = x;
System.out.println("x.i = " + x.i);
System.out.println("y.i = " + y.i);
System.out.println("Calling f(x_x)");
f(y);
System.out.println("x.i = " + x.i);
System.out.println("y.i = " + y.i);
}
}

2创建本地副本

Java中所有参数传递都是通过传递引用实现的。也就是说,当你传递“一个对象”时,你实际上传递的是存活于方法外部的指向这个对象的一个引用。因此,如果你通过该引用执行了任何修改,你也将修改外部的对象。

  • 引用别名会在传递参数时自动修改
  • 并没有本地对象,只有本地引用
  • 引用是有作用域的,对象则没有
  • Java中的对象生命周期从来就不是个问题

2.1值传递

得到一个本地的副本。

  1. Java传递任何事物时,都是在传递该事物的值。
  2. Java在传递基本类型时,传递的是值,但在传递对象时,传递的则是引用。

2.2克隆对象

创建对象的本地副本,最可能的原因时你要修改该对象,但并不想修改调用者的原有对象。这个时候就可以使用clone()方法了。

/**
* @Author Coder_Pans
* @Date 2022/11/20 10:38
* @PackageName:org.example.onjava.senior.example02ref
* @ClassName: CloneArrayList
* @Description: TODO clone()操作
* @Version 1.0
*/
class Int{
private int i; public Int(int i) {
this.i = i;
}
public void increment(){
i++;
} @Override
public String toString() {
return Integer.toString(i);
}
}
public class CloneArrayList {
public static void main(String[] args) {
ArrayList<Int> v = IntStream.range(0, 10)
.mapToObj(Int::new)
.collect(Collectors.toCollection(ArrayList::new));
System.out.println("v = " + v);
@SuppressWarnings("unchecked")
ArrayList<Int> v2 = (ArrayList<Int>)v.clone();
// Increment all v2's elements:
v2.forEach(Int::increment);// 对v2中的元素进行自增
// See if it changed v's elements:
System.out.println("v: " + v);// 这时会发现v中的元素都+1
}
}

clone()方法生成了一杯Object,然后该Object必须被转换为合适的类型。本例演示了ArrayList的clone()方法如何不去自动尝试克隆ArrayList中的每个对象——原有的ArrayList和克隆的ArrayList都是同一个对象的不同引用别名。这是一种浅拷贝(Shallow copy),因为只复制了对象的“表层”部分。实际对象的组成部分包括该“表层(引用)”、该引用指向的所有对象,以及所有这些对象所指向的所有对象,以此类推。

这通常称为“对象网络”。创建所有这些内容的完整副本,称为“深拷贝(deep copy)”

2.3为类增加可克隆能力

并不是所有类自动具备克隆能力,为了让某个类具备克隆能力,就必须专门添加代码来使其具备克隆的能力:

  • 利用protected的技巧

    • 调用super.clone()
    • 将你的克隆操作设为public
  • 实现Cloneable接口

2.4成功的克隆

了解了实现clone()方法的细节,就能够创建出可轻松复制的类,以提供生成本地副本的能力

/**
* @Author Coder_Pans
* @Date 2022/11/20 11:00
* @PackageName:org.example.onjava.senior.example02ref
* @ClassName: LocalCopy
* @Description: TODO 用clone()创建本地副本
* @Version 1.0
*/
class Duplo implements Cloneable{
private int n; public Duplo(int n) {
this.n = n;
} @Override
public Duplo clone() {// 为了使cloen能被访问,设置为public
try {
return (Duplo) super.clone();// 上面说到的调用super.clone(),协变返回类型。
} catch (CloneNotSupportedException e) {
throw new AssertionError();
}
}
public int getValue(){
return n;
}
public void setValue(int n){
this.n = n;
}
public void increment(){
n++;
} @Override
public String toString() {
return Integer.toString(n);
}
}
public class LocalCopy {
public static Duplo g(Duplo v){
// 传递引用,修改了外部的对象
v.increment();
return v;
}
public static Duplo f(Duplo v){
v = v.clone(); //本地副本,由于方法进行了协变返回,此处就不需要转型
v.increment();
return v;
} public static void main(String[] args) {
Duplo a = new Duplo(11);
Duplo b = g(a);
// Reference equivalence, not object equivalence:
// 引用相等,并不是对象相等
System.out.println("a == b: " + (a == b) +
"\na = " + a + "\nb = " + b);
System.out.println("a.equals(b) = " + a.equals(b));
Duplo c = new Duplo(47);
Duplo d = f(c);// 克隆c赋值给d
System.out.println("c == d: " + (c == d) +
"\nc = " + c + "\nd = " + d);
System.out.println("c.equals(d) = " + c.equals(d));
}
}
/**
* Output:
* a == b: true
* a = 12
* b = 12
* a.equals(b) = true
* c == d: false
* c = 47
* d = 48
* c.equals(d) = false
*/

首先,为了使clone()能被访问,必须将它设为public的。其次,在你的clone()操作的开始部分,调用基类版本的clone()。这里调用的clone()即在Object中预先定义好的那个clone(),你可以调用该方法,是因为它是protected的,可以在子类中被访问。

Object.clone()会检测出该对象有多大,并为新对象创建足够的内存空间,然后将旧对象所有的二进制都复制到新对象中。这称为按位复制。在这个方法执行之前,会先检查(在继承层次结构中)是否有类是Cloneable的,也就是它是否实现了Cloneable接口。如果没有,Object.clone()会抛出CloneNotSupportedException异常,表示无法进行克隆。

2.5Object.clone()的效果

根类中的clone方法负责创建正确大小的存储空间,并执行了从原始对象中的所有二进制位到新对象存储中的按位复制

也就是说,该方法并不只是创建存储空间和复制一个Object,它实际上会判断要复制的实际对象(不只是基类对象,还包括派生对象)的大小,然后再进行复制。

所有这些都依靠在根类中定义的clone()方法的代码来实现,而根类并不知道具体哪个对象会被继承,在这个过程中用到了反射来确定要克隆的实际对象。

这样,clone()方法就可以创建大小合适的存储空间 ,并正确地对该类型执行按位复制

/**
* @Author Coder_Pans
* @Date 2022/11/20 11:25
* @PackageName:org.example.onjava.senior.example02ref
* @ClassName: Snake
* @Description: TODO
* @Version 1.0
*/
public class Snake implements Cloneable{
private Snake next;// 单向链表
private char c; public Snake(int i, char x) {
this.c = x;
if (--i > 0){
next = new Snake(i, (char)(x + 1));
}
}
public void increment(){
c++;
if (next != null){
next.increment();// 蛇还活着,自己变长
}
} @Override
public String toString() {
String s = ":" + c;
if (next!= null){
s += next.toString();
}
return s;
} @Override
public Snake clone() {
try {
Snake clone = (Snake) super.clone();
// TODO: 在此处复制可变状态,因此克隆无法更改原始状态的内部
// TODO: copy mutable state here, so the clone can't change the internals of the original
return clone;
} catch (CloneNotSupportedException e) {
throw new AssertionError();
}
} public static void main(String[] args) {
Snake s = new Snake(5, 'a');
System.out.println("s = " + s);
Snake s2 = s.clone();
System.out.println("s2 = " + s2);
s.increment();
System.out.println(
"after s.increment, s2 = " + s2);
}
}

2.6克隆组合对象

在你试图深拷贝组合对象时,会遇到一个问题。你必须假设所有成员对象中clone()方法都会按顺序对各自的引用执行深拷贝,并照此进行下去。这一点是必须确保的。它实际上意味着为了正确执行深拷贝,你要么控制所有类的所有代码,要么至少对深拷贝涉及的所有类都足够了解,以确定它们都能正确地执行各自的深拷贝。

定义一个 DepthReading;

package org.example.onjava.senior.example02ref.clone_combined;

/**
* @Author Coder_Pans
* @Date 2022/11/20 13:20
* @PackageName:org.example.onjava.senior.example02ref.clone_combined
* @ClassName: DepthReading
* @Description: TODO 克隆组合对象01
* @Version 1.0
*/
public class DepthReading implements Cloneable{
private double depth; public DepthReading(double depth) {
this.depth = depth;
} public double getDepth() {
return depth;
} public void setDepth(double depth) {
this.depth = depth;
} @Override
public DepthReading clone() {
try {
DepthReading clone = (DepthReading) super.clone();
// TODO: copy mutable state here, so the clone can't change the internals of the original
return clone;
} catch (CloneNotSupportedException e) {
throw new AssertionError();
}
} @Override
public String toString() {
return String.valueOf(depth);
}
}

定义一个 TemperatureReading:

package org.example.onjava.senior.example02ref.clone_combined;

/**
* @Author Coder_Pans
* @Date 2022/11/20 13:22
* @PackageName:org.example.onjava.senior.example02ref.clone_combined
* @ClassName: TemperatureReading
* @Description: TODO 克隆组合对象02
* @Version 1.0
*/
public class TemperatureReading implements Cloneable{
private long time;
private double temperature; public TemperatureReading(double temperature) {
time = System.currentTimeMillis();// 获取当前系统时间
this.temperature = temperature;
} @Override
public TemperatureReading clone() {
try {
TemperatureReading clone = (TemperatureReading) super.clone();
// TODO: copy mutable state here, so the clone can't change the internals of the original
return clone;
} catch (CloneNotSupportedException e) {
throw new AssertionError();
}
} public long getTime() {
return time;
} public void setTime(long time) {
this.time = time;
} @Override
public String toString() {
return String.valueOf(temperature);
} public double getTemperature() {
return temperature;
} public void setTemperature(double temperature) {
this.temperature = temperature;
}
}

在定义一个拷贝上面两个对象的类,这个类在构造方法中初始化上面两个类

package org.example.onjava.senior.example02ref.clone_combined;

/**
* @Author Coder_Pans
* @Date 2022/11/20 13:23
* @PackageName:org.example.onjava.senior.example02ref.clone_combined
* @ClassName: OceanReading
* @Description: TODO
* @Version 1.0
*/
public class OceanReading implements Cloneable{
private DepthReading depth;
private TemperatureReading temperature; public OceanReading(double tdata, double ddata) {
this.depth = new DepthReading(ddata);
this.temperature = new TemperatureReading(tdata);
} /**
* 这里必须对 《构造方法》 中的克隆对象进行 克隆引用
* @return
*/
@Override
public OceanReading clone() {
OceanReading or = null;
try {
or = (OceanReading) super.clone();
} catch (CloneNotSupportedException e) {
throw new AssertionError();
}
// 必须克隆引用:
or.depth = (DepthReading)or.depth.clone();
or.temperature = (TemperatureReading)or.temperature.clone();
return or;
} public DepthReading getDepth() {
return depth;
} @Override
public String toString() {
return "depth=" + depth +
",temperature=" + temperature;
} public void setDepth(DepthReading depth) {
this.depth = depth;
} public TemperatureReading getTemperature() {
return temperature;
} public void setTemperature(TemperatureReading temperature) {
this.temperature = temperature;
}
}

下面对拷贝组合对象进行测试:

class DeepCopyTest {

    @Test
public void testClone(){
OceanReading reading = new OceanReading(33.9, 100.5);
// 进行克隆
OceanReading clone = reading.clone();
TemperatureReading tr = clone.getTemperature();
tr.setTemperature(tr.getTemperature() + 1);
clone.setTemperature(tr); DepthReading dr = clone.getDepth();
dr.setDepth(dr.getDepth() + 1);
clone.setDepth(dr);
assertEquals(reading.toString(),"depth=100.5,temperature=33.9");
assertEquals(clone.toString(),"depth=101.5,temperature=34.9");
} }

DepthReading和TemperatureReading很相似,它们都只包含基本类型。因此,clone()方法可以很简单:调用super.clone(),然后返回结果。这两者的clone是完全相同的。

OceanReading是由DepthReading和TemperatureReading对象组合而成的,因此,要实现深拷贝,OceanReading的clone()就必须克隆OceanReading内部的所有引用才行。要完成这项任务,super.clone()的结果必须转型为OceanReading对象(这样才可以访问depth和tempterature的引用)

2.7深拷贝ArrayList

对ArrayList进行深拷贝:

package org.example.onjava.senior.example02ref.clone_arraylist;

import java.util.ArrayList;
import java.util.stream.Collectors;
import java.util.stream.IntStream; /**
* @Author Coder_Pans
* @Date 2022/11/20 13:54
* @PackageName:org.example.onjava.senior.example02ref.clone_arraylist
* @ClassName: AddingClone
* @Description: TODO 深拷贝ArrayList
* @Version 1.0
*/
class Int2 implements Cloneable{
private int i; public Int2(int i) {
this.i = i;
}
public void increment(){
i++;
} @Override
public String toString() {
return Integer.toString(i);
} @Override
public Int2 clone() {
try {
Int2 clone = (Int2) super.clone();
// TODO: copy mutable state here, so the clone can't change the internals of the original
return clone;
} catch (CloneNotSupportedException e) {
throw new AssertionError();
}
}
}
// 继承不会移除可克隆性
class Int3 extends Int2{
private int j;// 自动创建了副本 public Int3(int i) {
super(i);
}
}
public class AddingClone {
@SuppressWarnings("unchecked")
public static void main(String[] args) {
Int2 x = new Int2(10);
Int2 x2 = x.clone();
x2.increment();
System.out.println(
"x = " + x + ", x2 = " + x2);
// 继承出的任何事物同样也是可克隆的
Int3 x3 = new Int3(7);
x3 = (Int3) x3.clone();
System.out.println("x3 = " + x3);
Int2 clone = x3.clone();
System.out.println("clone = " + clone); ArrayList<Int2> v = IntStream.range(0, 10)
.mapToObj(Int2::new)
.collect(Collectors.toCollection(ArrayList::new));
System.out.println("v = " + v);
ArrayList<Int2> v2 = (ArrayList<Int2>) v.clone();
// 现在克隆每个元素
IntStream.range(0, v.size())
.forEach(i -> v2.set(i, v.get(i).clone()));
v2.forEach(Int2::increment);
// 一旦克隆了一个对象,你就可以对副本进行修改,而原本对象不会受到影响
System.out.println("v2 = " + v2);
// 查看v中的元素是否发生改变
System.out.println("v = " + v);// 没有改变,复制成功
}
}
/**
* output:
* x = 10, x2 = 11
* x3 = 7
* clone = 7
* v = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
* v2 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
* v = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
*/

Int3继承自Int2,并增加了一个新的基本类型成员int j。在调用Int3的clone()时,其内部所调用的Int2的clone(),实际上调用的是Object.clone(),它检测到此处起作用的是Int3,并对Int3执行了按位复制。只要你不增加需要克隆的引用,对Object.clone()的一次调用便可以执行所有必要的复制,不论clone定义在继承层次结构中多么深的位置。

要深拷贝ArrayList,需要这么做:在克隆ArrayList后,还需要进一步克隆ArrayList所指向的每一个对象。如果要深拷贝入HashMap这样的内容,你也需要执行类似的操作。

一旦克隆了一个对象,你就可以对副本进行修改,而原本对象不会受到影响

2.8通过序列化进行深拷贝

如果一个对象先进行序列化,再将其反序列化,那么它实际上就是被克隆了。

接下来就使用序列化来实现深拷贝:

package org.example.onjava.senior.example02ref.clone_serializable;

import org.example.onjava.onjavaUtils.Timer;

import java.io.*;

/**
* @Author Coder_Pans
* @Date 2022/11/20 14:27
* @PackageName:org.example.onjava.senior.example02ref.clone_serializable
* @ClassName: Compete
* @Description: TODO 序列化实现深拷贝
* @Version 1.0
*/
class Thing1 implements Serializable {
} class Thing2 implements Serializable {
Thing1 t1 = new Thing1();
} class Thing3 implements Cloneable { @Override
public Thing3 clone() {
try {
return (Thing3) super.clone();
} catch (CloneNotSupportedException e) {
throw new AssertionError();
}
}
} class Thing4 implements Cloneable {
private Thing3 t3 = new Thing3(); @Override
public Thing4 clone() {
Thing4 t4 = null;
try {
t4 = (Thing4) super.clone();
} catch (CloneNotSupportedException e) {
throw new AssertionError();
}
//TODO 对字段也要进行克隆
t4.t3 = t3.clone();
return t4;// Thing4对象所包含的所有内容克隆完之后才能够返回。
}
} public class Compete {
public static final int SIZE = 10000; public static void main(String[] args) {
Thing2[] a = new Thing2[SIZE];
for (int i = 0; i < SIZE; i++) {
a[i] = new Thing2();
} Thing4[] b = new Thing4[SIZE];
for (int i = 0; i < SIZE; i++) {
b[i] = new Thing4();
}
Timer timer = new Timer();
try (ByteArrayOutputStream buf = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(buf)) {
for (Thing2 a1 : a) {
oos.writeObject(a1);
}
// Now get copies:
try (ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(buf.toByteArray()))) {
Thing2[] c = new Thing2[SIZE];
for (int i = 0; i < SIZE; i++)
c[i] = (Thing2) in.readObject();
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
} catch (IOException e) {
throw new RuntimeException(e);
}
System.out.println("Duplication via serialization: " + timer.duration() + " Milliseconds");
// Now try cloning:
timer = new Timer();
Thing4[] d = new Thing4[SIZE];
for (int i = 0; i < SIZE; i++)
d[i] = b[i].clone();
System.out.println("Duplication via cloning: " + timer.duration() + " Milliseconds");
}
}

Thing2和Thing4包含了成员对象,所以需要进行一些深拷贝操作。Serializable类很容易构建,但是需要大量额外操作来复制它们。另外,在克隆所需要的操作中,类的构建工作更多,但是实际的对象复制操作相对简单

序列化比克隆慢的多得多!

Duplication via serialization: 115 Milliseconds
Duplication via cloning: 3 Milliseconds

Java-02对象传递和返回的更多相关文章

  1. Java中值传递和引用传递的概念

    很多书中都提到了在Java中只存在值传递,但是今天在一个NanoHTTPD的源码中看到这样一段: if (qmi >= 0) { decodeParms(uri.substring(qmi + ...

  2. java传递和返回对象

    java传递的只是一个引用,一定要注意准确认识在对象传递和赋值时所发生的一切. 事实上,java中的每个对象(除了基本数据类型以外)的标识符都属于指针的一种,但是其使用受到了严格的限制和防范,不仅在编 ...

  3. spring mvc返回json字符串数据,只需要返回一个java bean对象就行,只要这个java bean 对象实现了序列化serializeable

    1.spring mvc返回json数据,只需要返回一个java bean对象就行,只要这个java bean 对象实现了序列化serializeable 2. @RequestMapping(val ...

  4. java 对象传递 是 值传递 还是 引用传递?

    这个问题说实话我感觉没有太大的意义. 按第一印象和c++的一些思想去理解的话对象传递是引用传递,因为传递过去的对象的值能被改变. 但是又有很多人,不知道从哪里扣出来一句,java中只有值传递,没有引用 ...

  5. 2.6 C++通过引用来传递和返回类对象

    参考:http://www.weixueyuan.net/view/6338.html 总结: C++语言中,由类声明的对象,和其它类型声明的变量一样,同样可以通过传值.引用和指针的方式作为函数的参数 ...

  6. Java的Object.hashCode()的返回值到底是不是对象内存地址?

    关于这个问题,查阅了网上的资料,发现证明过程太繁琐,这里我用了反证法. java.lang.Object.hashCode()的返回值到底是不是对象内存地址? hashCode契约 说到这个问题,大家 ...

  7. 【java】值传递和引用传递---对象作为方法的参数传入属于哪种传递

    首先 这篇作为一个永久性的问题,欢迎大家讨论 其次,个人结论如下几条: ①Java有且只有一种传递,即 值传递 ②作为方法的参数传入,都是对原本的实参进行了copy ③只不过[实参]若是[基本数据类型 ...

  8. Java中对象、对象引用、堆、栈、值传递以及引用传递的详解

    Java中对象.对象引用.堆.栈.值传递以及引用传递的详解 1.对象和对象引用的差别: (1).对象: 万物皆对象.对象是类的实例. 在Java中new是用来在堆上创建对象用的. 一个对象能够被多个引 ...

  9. java的对象锁和对象传递

    1.对象传递 在JAVA的參数传递中,有两种类型,第一种是基本类型传递,比如int,float,double等,这样的是值传递,第二种是对象传递,比方String或者自己定义的类,这样的是引用传递. ...

  10. 064 01 Android 零基础入门 01 Java基础语法 08 Java方法 02 无参带返回值方法

    064 01 Android 零基础入门 01 Java基础语法 08 Java方法 02 无参带返回值方法 本文知识点:无参带返回值方法 说明:因为时间紧张,本人写博客过程中只是对知识点的关键步骤进 ...

随机推荐

  1. 关于promise经典面试题

    这里涉及到同步和异步的问题

  2. 2022-3-11内部群每日三题-清辉PMP

    1.供应商通知项目经理可能延迟交付一个模块.项目经理应该怎么做? A.立即通知相关方. B.通过增加额外的天数来修改项目管理计划,并记录它们对项目时间的影响. C.审查风险管理计划以评估风险,然后通知 ...

  3. jmeter非GUI模式运行-单节点

    jmeter有自己的GUI页面,但是当线程数很多或者现在有很多的测试场景都是基于linux下进行压测,这时我们可以使用jmeter的命令行方式来执行测试,该篇文章介绍jmeter单节点命令运行方式. ...

  4. golang实现请求cloudflare修改域名A记录解析

    现在有些DNS解析要收费,国内的几个厂商需要实名制.下面给出golang请求cloudflare修改域名A记录解析的代码. 准备工作: 在域名购买服务商处,将dns解析服务器改为cloudflare的 ...

  5. Unity多线程使用(线程池)

    1.在C#中使用线程池需要以下这个类库using System.Threading 2.开单个线程(unity程序停止前 线程一定要关闭) private Thread tempThread; voi ...

  6. 搭建python+appium环境的时候遇到 'could not find adb.exe!'的问题

    搭建Android环境的时候遇到 'could not find adb.exe!'的问题 如果是在的C:\android-sdk-windows\tools目录下并没有adb.exe这个可执行文件. ...

  7. python -m pip install --upgrade pip 解决升级不成功问题

    使用pip 提示更新: You are using pip version 18.1, however version 20.0.2 is available. You should consider ...

  8. 25 bootstrap--v3--datetimepicker时间选择器--应用

    在模板中引用响应的文件 比如: layout.html <link rel="stylesheet" href="{% static 'stark/plugins/ ...

  9. 招新题流程简介(WS2812)

    22物电科协软件招新题学习流程 有错误或者不当的地方请在评论区指出 题目简介 使用stm32驱动单一ws2812b灯珠实现呼吸灯效果,驱动及实现方法不限 演示效果 快速入门,在stm32核心板上点灯 ...

  10. 【python】第一模块 步骤五 第一课、内存管理机制

    第一课.内存管理机制 一.课程介绍 1.1 课程概要 课程概要 赋值语句的内存分析 垃圾回收机制 内存管理机制 课程目标 掌握赋值语句内存分析方法 掌握id()和is()的使用 了解python的垃圾 ...