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. css内容超出元素高度可滚动并且隐藏滚动条

    .div::-webkit-scrollbar { display: none; / Chrome Safari / } .div{ scrollbar-width: none; / firefox ...

  2. SSIS Package Version

    当 SSIS 首次出现时,有大量关于所有问题的笑话和帖子,以及每个人如何认为 DTS 更好,他们真的必须转换吗?多年来,我开始欣赏 SSIS.它是一个非常强大和有用的工具,可以做一些了不起的事情.当然 ...

  3. 2.TS 的数据类型

    一.定义变量 TS 中创建变量有四个要素: 定义变量的关键字:var.let.const 变量名 变量的数据类型 变量的初始值 语法结构如下: let 变量名: 数据类型 = 初始值 注意,变量的初始 ...

  4. ZSTUOJ刷题⑧:Problem G.--铺地砖

    Problem G: 铺地砖 Time Limit: 1 Sec  Memory Limit: 64 MBSubmit: 6461  Solved: 2592 Description 元旦过去了,新年 ...

  5. 1903021126 申文骏 Java 第二周作业 代码编写及运行

    项目 内容 课程班级博客链接 19级信计班(本) 作业要求链接 Java第二周作业 博客名称 1903021126 申文骏 Java 第二周作业 代码编写及运行 要求 每道题要有题目,代码(使用插入代 ...

  6. Zabbix 监控服务介绍

    Zabbix 监控服务介绍 目录 Zabbix 监控服务介绍 一.Zabbix 监控服务介绍 1.1.1 Zabbix 监控服务介绍 1.1.2 如何去做监控 1.13 硬件监控 1.1.4 系统监控 ...

  7. quartus报错 Error (10054): Verilog HDL File I/O error at sdram_ctrl_tb.v(6): can't open Verilog Design File "Sdram_params.h"

    解决方法:包含完整路径. 比如我一开始是:`include "Sdram_params.h" 错误(改为:`include "F:\FPGA\exce\uart2sdra ...

  8. Vue中nextTick的时序问题

    前言 Vue.$nextTick这个API相信很多人都用过,按照文档的解释,"在下次DOM更新循环结束之后执行延迟回调.在修改数据之后立即使用这个方法,获取更新后的DOM".我们通 ...

  9. 2003031118—李伟—Python数据分析五一假期作业—MySQL的安装以及使用

            项目  期中试卷 课程班级博客链接 20级数据班(本) 这个作业要求链接 作业要求 博客名称 2003031118-李伟-Python数据分析五一假期作业-MySQL的安装以及使用 要 ...

  10. 解决SpringBoot前后端集成项目导出jar包运行访问页面资源报错“Whitelabel Error Page”问题

    一.SpringBoot前后端集成项目导出jar包后运行访问页面资源报错"Whitelabel Error Page"问题 二.解决方案 1.将Controller层移入com.x ...