线程间操作无效: 从不是创建控件“textBox1”的线程访问它(未处理System.InvalidOperationException)(委托)delegate,Invoke,BeginInvoke

本文主要是介绍线程间操作无效: 从不是创建控件“textBox1”的线程访问它(未处理System.InvalidOperationException)(委托)delegate,Invoke,BeginInvoke,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

 

目录

1.问题复现

2.分析问题

3.解决办法

4.写法拓展

5.总结与源码

//长话短说 //本文将逐步的的引导并解决此问题//大家互相交流,有不足处请指正

//源码在本文最后面

 

  1. 问题复现

新建WinForm程序 ,界面 Form1,拖个 button1 和textBox1,然后Button1中增加click事件,其中Form1.cs中部分代码如下:

private void button1_Click(object sender, EventArgs e)

{

    System.Threading.Thread Thread_1 = new System.Threading.Thread(ThreadOne);

    Thread_1.IsBackground = true//设置为后台线程,软件关闭则线程关闭

    Thread_1.Start();  //线程中若想传值可此处将值传过去,比如我想传个字符串的值,此处可 Thread_1.Start("传值")

}

private void ThreadOne() //线程中若想传值可设置为object类型,即 ThreadOne(object obj)

{

    MessageBox.Show("即将运行报错的代码!");

    textBox1.Text = " 在这个线程里改变控件的值"; //会报异常   Message=线程间操作无效: 从不是创建控件“textBox1”的线程访问它。

}

编译运行后如下图,复现此BUG:

文字提示如下:

未处理System.InvalidOperationException

  Message=线程间操作无效: 从不是创建控件“textBox1”的线程访问它。

  Source=System.Windows.Forms

  StackTrace:

       在 System.Windows.Forms.Control.get_Handle()

       在 System.Windows.Forms.Control.set_WindowText(String value)

       在 System.Windows.Forms.TextBoxBase.set_WindowText(String value)

2.分析问题

仔细看错误提示为: 线程间操作无效: 从不是创建控件“textBox1”的线程访问它。

这句话字面意思是这个控件只能被创建它的线程访问,即线程A中创建了控件A1,若线程B中想修改控件A1的属性,则不允许!(如下图)

3.解决办法

那么能否找个中间变量或者在A线程中有某种方法可以让线程B中执行的某段代码可以从线程B中跳到线程A中,然后线程A改控件A1中的信息 ,这样就是A改A中创建的控件A1了,如下图

那么这个中间变量是什么呢?此时就用到了关键字 delegate(委托),

然后我们试试 通过用这个关键字可以解决我们遇到的问题吗?同时我们也得确认下他提示的”不是从创建的线程中访问它”这句话是否正确,既然是线程,那么我们就在System.Threading.Thread下面搜,然后就我们搜到了如下信息:

可获得当前唯一标识符(ID)的代码:

int NowThreadID = System.Threading.Thread.CurrentThread.ManagedThreadId;

可获得当前工作的线程的名字(Name)的代码:

 string NowThreadName = System.Threading.Thread.CurrentThread.Name;

仔细看描述,发现唯一标识符(ID)只读,不可设,而当前工作的线程的名字(Name)可以设置的,那我们就选择Name来判断当前工作的线程是哪个。然后我们新建个WinForm程序,然后在第一个桌面程序Form1的构造函数中提前设置主线程Name为MainThread

代码如下

public Form1()

{

   InitializeComponent();

   System.Threading.Thread.CurrentThread.Name = "MainThread"; //备注:软件开启后只能设置一次

}

然后设计器中拖个button2新增个click事件,

提前设置好子线程的Name为:ThreadTwo声明一个叫SetText_delegate的委托,但是目前这个委托不知道怎么调用啊,既然不知道,那先按普通的方法那样调用,

相关代码如下

private void button2_Click(object sender, EventArgs e)

{

    MessageBox.Show("当前线程Name:" + System.Threading.Thread.CurrentThread.Name);

    System.Threading.Thread Thread_2 = new System.Threading.Thread(ThreadTwo);

    Thread_2.Name = "ThreadTwo";   //设置子线程Name

Thread_2.IsBackground = true//设置为后台线程,软件关闭则线程关闭

    Thread_2.Start();

}

private delegate void SetText_delegate(string sssext); //声明一个委托  //相当于在此线程中定义一个方法p

rivate void SetText(string str)

{

    MessageBox.Show("哈哈,跳到这里来了,当前线程Name:" + System.Threading.Thread.CurrentThread.Name);

    textBox1.Text = str + ",赋值部分!";

}

private void ThreadTwo()

{

    MessageBox.Show("正常定义委托,即将更改值!!" + "当前线程Name:" + System.Threading.Thread.CurrentThread.Name);

    SetText_delegate aInThread = new SetText_delegate(SetText);  //核心代码

aInThread("线程中直接传递参数,报错"); //报错:此处直接调用则当前工作线程还是子线程

}

以上代码运行过后发现在aInThread("线程中直接传递参数,报错"); 处还是会报同样的错误

提取部分报错内容如下:

Message=线程间操作无效: 从不是创建控件“textBox1”的线程访问它。

在 WindowsFormsApplication1.Form1.SetText(String str) 位置 d:\Projects\C#\WindowsFormsApplication1\WindowsFormsApplication1\Form1.cs:行号 91

在 WindowsFormsApplication1.Form1.ThreadTwo() 位置 d:\Projects\C#\WindowsFormsApplication1\WindowsFormsApplication1\Form1.cs:行号 100

上面报错验证了子线程中委托方法实例化后直接调用主线程中的方法是不可行的,仔细查看发现控件textBox1赋值前的MessageBox.Show中提示当前工作线程还是: ThreadTwo

直接调用走不通,那么是否是委托不行或者是我们不会用这个委托?

这时我们查到了关键字Invoke/BeginInvoke那么这个关键字的作用是什么?怎么用?

按F12或则在Invoke处右键选择转到定义,然后我们发现了下图信息,

关键字:

英文截图如下:

OK,这里写出了 这个Invoke使用的前提如下

  1. 在拥有控件的基础窗口句柄的线程上,(Executes the specified delegate,
  2. 用指定的参数列表执行指定委托(on the thread that owns the control's underlying window handle
  3. Invoke运行需要两个参数,一个是Delegate类型的,另一个是Object类型,即普通参数

可是我们如何用如何用Invoke呢,突然想到线程ThreadTwo也是处于MainThread中(即线程的Name,前文有写),

然后我们将线程中ThreadTwo方法内的aInThread方法注释掉,然后可改为如下:

private void ThreadTwo()

{

   MessageBox.Show("正常定义委托,即将更改值!!" + "当前线程Name:" + System.Threading.Thread.CurrentThread.Name);

   SetText_delegate aInThread = new SetText_delegate(SetText);  //核心代码

   MessageBox.Show("正常定义委托,即将更改值!!当前线程Name:" + System.Threading.Thread.CurrentThread.Name); ;

  //aInThread("线程中直接传递参数,报错"); //报错:此处直接调用则当前工作线程还是子线程

   this.Invoke(aInThread, "第一种写法,我要赋值了呦!");  //核心代码

   MessageBox.Show("正常定义委托,当前线程Name:" + System.Threading.Thread.CurrentThread.Name);

}

然后编译运行,成功赋值!成功将值赋给了textBox1

那么下图应改为如下:

准确来说应该如下图

通过以上我们知道了Invoke和委托二者缺一不可,只有运行到Invoke才会触发方法SetText给控件textBox1赋值,那么软件运行的顺序是什么呢?每运行一步我都加个MessageBox来查看软件的运行顺序,实际弹窗的顺序如下:

从上图中标记处,我们就很容易得出委托实例化时没有运行到方法内部,只有运行到Invoke时才会运行到方法SetText,进而改变textBox1属性,

或者看着麻烦也可以改成这样儿

即线程ThreadTwo中调用方法Test,方法Test仍然在子线程ThreadTwo中,而只有Invoke调用的委托函数SetTest在MainThread中,

 

那么现在我们定义了两个方法,方法SetText和方法Test,两个方法可否合二为一?

我们可以试着判断,若是仍然在线程ThreadTwo中则继续运行目前方法Test中的内容,若当前运行的线程为MainThread就对textBox1进行赋值,

主线程赋值代码:System.Threading.Thread.CurrentThread.Name = "MainThread"; //备注:软件开启后只能设置一次

那么方法可改为  

private void button2_Click(object sender, EventArgs e)

{

    MessageBox.Show("当前线程Name:" + System.Threading.Thread.CurrentThread.Name, "1");

    System.Threading.Thread Thread_2 = new System.Threading.Thread(ThreadTwo);

    Thread_2.Name = "ThreadTwo";   //设置子线程Name

    Thread_2.IsBackground = true//设置为后台线程,软件关闭则线程关闭

    Thread_2.Start();

}

private void ThreadTwo()

{

    NewTest("我要赋值了呦!");

}

private delegate void SetText_delegate(string sssext); //声明一个委托  //相当于在此线程中定义一个方法

private void NewTest(string str)

{

    if (!System.Threading.Thread.CurrentThread.Name.Equals("MainThread"))

    {

        SetText_delegate aInThread = new SetText_delegate(SetText);  //核心代码

        this.Invoke(aInThread, str + ",调用Invoke调用");  //核心代码

    }

    else

    {

        textBox1.Text = str + ",赋值了!";

    }

}

然后运行结果如下

那么如果我不会设置主线程的Name呢?

有方法:麻烦将

if (!System.Threading.Thread.CurrentThread.Name.Equals("MainThread"))

替换成

if (this.InvokeRequired)

试试看,哇塞,居然也可以,而且貌似替换后就变成了网上主流的方式。

4.多种写法

4.1网络正常写法及变种

网上主流的方式基本上是如下:

private void button2_Click(object sender, EventArgs e)

{

    System.Threading.Thread Thread_2 = new System.Threading.Thread(ThreadTwo);

    Thread_2.IsBackground = true//设置为后台线程,软件关闭则线程关闭

    Thread_2.Start();

}

private delegate void SetText_delegate(string sssext); //声明一个委托  //相当于在此线程中定义一个方法

private void ThreadTwo()

{

    NewTest("我要赋值了呦!");

}

private void NewTest(string str)

{

    //if (!System.Threading.Thread.CurrentThread.Name.Equals("MainThread"))

    if (this.InvokeRequired)

    {

        SetText_delegate aInThread = new SetText_delegate(SetText);  //核心代码

        this.Invoke(aInThread, str + ",调用Invoke调用");  //核心代码

    }

    else

    {

        textBox1.Text = str + ",赋值了!";

    }

}

那么有没有些变种写法呢?

有在Invoke/BeginInvoke处按F12或则在Invoke处右键选择转到定义,然后我们发现了下图信息,

    //public object Invoke(Delegate method);

    //public object Invoke(Delegate method, params object[] args);

    //public IAsyncResult BeginInvoke(Delegate method);

    //public IAsyncResult BeginInvoke(Delegate method, params object[] args);

意思就是无论什么方法,只要第一个参数是Delegate就行,

如果我们把网上的方法当作第0种写法,我前面写的当作第一种写法,其他的依次往后排:

private void ThreadTwo()

{

    //第0种写法

    NewTest("我要赋值了呦!");

 

    第一种写法

    SetText_delegate aInThread = new SetText_delegate(SetText_NoReturn);  //核心代码

    this.Invoke(aInThread, "第一种写法,我要赋值了呦!");  //核心代码

 

    //第二种写法

    SetText_delegate SetText_d = SetText_NoReturn;

    this.Invoke(SetText_d, new object[] { "第二种写法,我还要赋值了呦!" }); //变种方式如上

 

    //第三种写法

    this.Invoke(new SetText_delegate(SetText_NoReturn), "第三种写法,我又要赋值了呦!"); //变种方式如上

 

    //第四种写法        

    this.Invoke((SetText_delegate)SetText_NoReturn, "第四种写法,我再次要赋值了呦!"); //变种方式如上

 

    //第五种写法

    Action<string> ffaff = new Action<string>(SetText_NoReturn);

    this.Invoke(ffaff, "5555555555");

 

    //第六种写法

    Action<string> fffaff = SetText_NoReturn;

    this.Invoke(fffaff, "666666666666");

 

    //第七种写法

    this.Invoke(new Action<string>(SetText), "7777777777");

 

    //第八种写法   //Func是.NET里面的内置委托,它有很多重载。//第一个值是参数,第二个值是返回值

    Func<string, string> ssfada = new Func<string, string>(SetText_ReturnStr);

    this.Invoke(ssfada, "8888888888");

 

    //第九种写法

    Func<string, string> ssfaa = SetText_ReturnStr;

    this.Invoke(ssfaa, "999999999999");

 

    //第十种写法

    this.Invoke(new Func<string, string>(SetText_ReturnStr), "无论何种写法,Invoke第一个参数必须为Delegate类型");

 

    //以上this.textBox1.Invoke 可改为 this.textBox1.BeginInvoke 或者 this.Invoke 或者 this.BeginInvoke 功能相同

}

private delegate void SetText_delegate(string sssext); //声明一个委托  //相当于在此线程中定义一个方法

private void SetText_NoReturn(string str)

{

    textBox1.Text = str;

    MessageBox.Show(str);

}

private string SetText_ReturnStr(string str)

{

    textBox1.Text = str;

    MessageBox.Show(str);

    return str; //随便返回个字符串就行

}

那么有没有直接在线程里面直接可以操作的?

4.2 BeginInvoke的作用

相关代码如下,其中SetText_delegate本文前面定义过

private void button3_Click(object sender, EventArgs e)

{

    MessageBox.Show("当前线程Name:" + System.Threading.Thread.CurrentThread.Name, "1");

    System.Threading.Thread Thread_3 = new System.Threading.Thread(ThreadThree);

    Thread_3.Name = "ThreadThree";

    Thread_3.IsBackground = true;

    Thread_3.Start();

}

private void ThreadThree()

{                  //System.Threading.Thread.CurrentThread.Name 值为 ThreadThree

    MessageBox.Show("进入线程中,当前线程Name:" + System.Threading.Thread.CurrentThread.Name, "2");

    string strargs = "我要赋 值了呦! " + button3.Text;

    SetText_delegate setText = delegate(string str)

    {                                                        //MainThread  //这就说明delegate只是起到了函数指针的作用

        MessageBox.Show("即将改值,当前线程Name:" + System.Threading.Thread.CurrentThread.Name, "4");

        this.textBox1.Text = str;

    };

    MessageBox.Show("线程运行中,线程:" + System.Threading.Thread.CurrentThread.Name, "3");

    this.BeginInvoke(setText, new object[] { "赋值" });

    MessageBox.Show("线程运行中,线程:" + System.Threading.Thread.CurrentThread.Name, "5");

}

 

BeginInvoke运行结果如下:

 

4.3 超简写

那么有没有可以用最小行数的代码来实现此功能呢?

答:有,代码如下(本文最后有源码下载链接)

#region 委托(超简写)

private delegate void delegate_ParameterOne(string s);  //带一个参数的

private delegate void delegate_ParameterNo();         //不带参数的

private void button4_Click(object sender, EventArgs e)

{

    System.Threading.Thread Thread_4 = new System.Threading.Thread(ThreadFour);

    Thread_4.Name = "ThreadFour";

    Thread_4.IsBackground = true;

    Thread_4.Start();

}

private void ThreadFour()

{

    //我们都知道Invoke有两种用法,那么什么时候用一个参数,什么时候用两个参数呢?            //请往下看

    //public object Invoke(Delegate method);

    //public object Invoke(Delegate method, params object[] args);

    MessageBox.Show("即将开始改变textBox1的值");

    int n = 0; string strMessage = "";

    //以下方法中,通过Invoke的第二个参数来传值    strMessage 等同于  new object[] { strMessage }

    n = 1; strMessage = string.Format("这是第 {0} 种写法", n);

    delegate_ParameterOne setText = delegate(string str) { this.textBox1.Text = str; };

    this.Invoke(setText, strMessage);

    MessageBox.Show(strMessage);

 

    n = 2; strMessage = string.Format("这是第 {0} 种写法", n);

    this.Invoke((delegate_ParameterOne)(delegate(string str) { this.textBox1.Text = str; }), strMessage);

    MessageBox.Show(strMessage);

 

    n = 3; strMessage = string.Format("这是第 {0} 种写法", n);

    this.Invoke((delegate_ParameterOne)((string str) => { this.textBox1.Text = str; }), strMessage);

    MessageBox.Show(strMessage);

 

    n = 4; strMessage = string.Format("这是第 {0} 种写法", n);

    this.Invoke(new delegate_ParameterOne(delegate(string str) { this.textBox1.Text = str; }), strMessage);

    MessageBox.Show(strMessage);

 

    n = 5; strMessage = string.Format("这是第 {0} 种写法", n);

    this.Invoke(new delegate_ParameterOne((string str) => { this.textBox1.Text = str; }), strMessage);

    MessageBox.Show(strMessage);

 

    //以下是不带参数而传值的写法

    n = 6; strMessage = string.Format("这是第 {0} 种写法", n);

    delegate_ParameterNo setT = delegate() { this.textBox1.Text = strMessage; };

    this.Invoke(setT);

    MessageBox.Show(strMessage);

 

    n = 7; strMessage = string.Format("这是第 {0} 种写法", n);

    this.Invoke((delegate_ParameterNo)(delegate() { this.textBox1.Text = strMessage; }));

    MessageBox.Show(strMessage);

 

    n = 8; strMessage = string.Format("这是第 {0} 种写法", n);

    this.Invoke((delegate_ParameterNo)(() => { this.textBox1.Text = strMessage; }));

    MessageBox.Show(strMessage);

 

    n = 9; strMessage = string.Format("这是第 {0} 种写法", n);

    this.Invoke(new delegate_ParameterNo(delegate() { this.textBox1.Text = strMessage; }));

    MessageBox.Show(strMessage);

 

    n = 10; strMessage = string.Format("这是第 {0} 种写法", n);

    this.Invoke(new delegate_ParameterNo(() => { this.textBox1.Text = strMessage; }));

    MessageBox.Show(strMessage);

 

    n = 11; strMessage = string.Format("这是第 {0} 种写法", n);

    this.Invoke(new delegate_ParameterOne((string str) => { this.textBox1.Text = strMessage; }), new object[] { null });

    //注意:第二个参数直接接写null会报错

    MessageBox.Show(strMessage);

 

    //通过以上十一个方法我们总结出了如何用哪种Invoke中取决于 定义delegate类型的方法时候带不带参数

    //如不带,即像定义delegate_ParameterNo时需要用一个参数的Invoke,即

    //public object Invoke(Delegate method);

    //如带参数,即像定义delegate_ParameterOne时需要用两个个参数的Invoke,即

    //public object Invoke(Delegate method, params object[] args);

 

    n = 12; strMessage = string.Format("这是第 {0} 种写法", n);

    this.Invoke((Func<string, string>)delegate(string str) { textBox1.Text = str; return ""; }, strMessage);

    MessageBox.Show(strMessage);  //Func中第二个参数为返回值类,可改

 

    n = 13; strMessage = string.Format("这是第 {0} 种写法", n);

    this.Invoke((Func<string, string>)delegate { textBox1.Text = strMessage + "," + strMessage; return ""; }, "此变量无作用");

    MessageBox.Show(strMessage);  //Func中第二个参数为返回值类,可改

 

    n = 14; strMessage = string.Format("这是第 {0} 种写法", n);

    this.Invoke((Func<string, string>)delegate { textBox1.Text = strMessage; return ""; }, "");

    MessageBox.Show(strMessage);

 

    n = 15; strMessage = string.Format("这是第 {0} 种写法", n);

    this.Invoke(new Func<string>(() => { textBox1.Text = strMessage; return ""; }));

    MessageBox.Show(strMessage);

 

    n = 16; strMessage = string.Format("这是第 {0} 种写法", n);

    this.Invoke(new MethodInvoker(delegate() { textBox1.Text = strMessage; }));

    MessageBox.Show(strMessage);

 

    n = 17; strMessage = string.Format("这是第 {0} 种写法", n);

    this.Invoke(new Action(delegate { textBox1.Text = strMessage; }));

    MessageBox.Show(strMessage);

 

    n = 18; strMessage = string.Format("这是第 {0} 种写法", n);//lambda表达式

    this.Invoke(new Action(() => { textBox1.Text = strMessage; }));

    MessageBox.Show(strMessage);

 

    n = 19; strMessage = string.Format("这是第 {0} 种写法", n);

    this.Invoke(new EventHandler(delegate { textBox1.Text = strMessage; }));

    MessageBox.Show(strMessage);

 

    n = 20; strMessage = string.Format("这是第 {0} 种写法", n);

    this.Invoke((EventHandler)delegate { textBox1.Text = strMessage; });

    MessageBox.Show(strMessage);

 

    MessageBox.Show("总结,写法再多也是为了实现功能,记不住就记网上最经典的方法好了,一法通万法通!", "总结");

}

#endregion

 

5.总结与源码

5.1总结

对于本文题目的问题,个人认为我们应该主要了解及怎么使用Invoke/BeginVoke,其次才是delegate的使用。

总结本博文前半部分主要写了delegate的用法,及如何使用Invoke,什么情况下使用Invoke,怎么使用Invoke。

本文中间写了InvokeRequired的作用,

后半部分写了Beginvoke的作用,捎带提了Action的用法,Func的用法,EventHandler的用法。

其实还是有种方法关闭线程安全检查,即

System.Windows.Forms.Control.CheckForIllegalCrossThreadCalls = false; //关闭安全检查

极其不建议如此做,原因关闭安全检查是就相当于 消防检查出问题了,但是你每次都忽略。

5.2源码:

源代码为Visual Studio 2012环境下编译的

源代码链接:

https://download.csdn.net/download/shengmingzaiyuxuexi/12274875(推荐)

https://download.csdn.net/download/shengmingzaiyuxuexi/12264023(前一版本,仅供借鉴)

若没积分,请留言或私信。

 

 

本文参考链接:https://docs.microsoft.com/zh-cn/previous-versions/visualstudio/visual-studio-2008/ms171728(v=vs.90)?redirectedfrom=MSDN

 

 

有错误,请指正

有疑问,请留言

与君共勉,共同进步!

这篇关于线程间操作无效: 从不是创建控件“textBox1”的线程访问它(未处理System.InvalidOperationException)(委托)delegate,Invoke,BeginInvoke的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



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

相关文章

无人叉车3d激光slam多房间建图定位异常处理方案-墙体画线地图切分方案

墙体画线地图切分方案 针对问题:墙体两侧特征混淆误匹配,导致建图和定位偏差,表现为过门跳变、外月台走歪等 ·解决思路:预期的根治方案IGICP需要较长时间完成上线,先使用切分地图的工程化方案,即墙体两侧切分为不同地图,在某一侧只使用该侧地图进行定位 方案思路 切分原理:切分地图基于关键帧位置,而非点云。 理论基础:光照是直线的,一帧点云必定只能照射到墙的一侧,无法同时照到两侧实践考虑:关

【Python编程】Linux创建虚拟环境并配置与notebook相连接

1.创建 使用 venv 创建虚拟环境。例如,在当前目录下创建一个名为 myenv 的虚拟环境: python3 -m venv myenv 2.激活 激活虚拟环境使其成为当前终端会话的活动环境。运行: source myenv/bin/activate 3.与notebook连接 在虚拟环境中,使用 pip 安装 Jupyter 和 ipykernel: pip instal

安卓链接正常显示,ios#符被转义%23导致链接访问404

原因分析: url中含有特殊字符 中文未编码 都有可能导致URL转换失败,所以需要对url编码处理  如下: guard let allowUrl = webUrl.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) else {return} 后面发现当url中有#号时,会被误伤转义为%23,导致链接无法访问

在cscode中通过maven创建java项目

在cscode中创建java项目 可以通过博客完成maven的导入 建立maven项目 使用快捷键 Ctrl + Shift + P 建立一个 Maven 项目 1 Ctrl + Shift + P 打开输入框2 输入 "> java create"3 选择 maven4 选择 No Archetype5 输入 域名6 输入项目名称7 建立一个文件目录存放项目,文件名一般为项目名8 确定

【生成模型系列(初级)】嵌入(Embedding)方程——自然语言处理的数学灵魂【通俗理解】

【通俗理解】嵌入(Embedding)方程——自然语言处理的数学灵魂 关键词提炼 #嵌入方程 #自然语言处理 #词向量 #机器学习 #神经网络 #向量空间模型 #Siri #Google翻译 #AlexNet 第一节:嵌入方程的类比与核心概念【尽可能通俗】 嵌入方程可以被看作是自然语言处理中的“翻译机”,它将文本中的单词或短语转换成计算机能够理解的数学形式,即向量。 正如翻译机将一种语言

Java 创建图形用户界面(GUI)入门指南(Swing库 JFrame 类)概述

概述 基本概念 Java Swing 的架构 Java Swing 是一个为 Java 设计的 GUI 工具包,是 JAVA 基础类的一部分,基于 Java AWT 构建,提供了一系列轻量级、可定制的图形用户界面(GUI)组件。 与 AWT 相比,Swing 提供了许多比 AWT 更好的屏幕显示元素,更加灵活和可定制,具有更好的跨平台性能。 组件和容器 Java Swing 提供了许多

顺序表之创建,判满,插入,输出

文章目录 🍊自我介绍🍊创建一个空的顺序表,为结构体在堆区分配空间🍊插入数据🍊输出数据🍊判断顺序表是否满了,满了返回值1,否则返回0🍊main函数 你的点赞评论就是对博主最大的鼓励 当然喜欢的小伙伴可以:点赞+关注+评论+收藏(一键四连)哦~ 🍊自我介绍   Hello,大家好,我是小珑也要变强(也是小珑),我是易编程·终身成长社群的一名“创始团队·嘉宾”

Maven创建项目中的groupId, artifactId, 和 version的意思

文章目录 groupIdartifactIdversionname groupId 定义:groupId 是 Maven 项目坐标的第一个部分,它通常表示项目的组织或公司的域名反转写法。例如,如果你为公司 example.com 开发软件,groupId 可能是 com.example。作用:groupId 被用来组织和分组相关的 Maven artifacts,这样可以避免

两个月冲刺软考——访问位与修改位的题型(淘汰哪一页);内聚的类型;关于码制的知识点;地址映射的相关内容

1.访问位与修改位的题型(淘汰哪一页) 访问位:为1时表示在内存期间被访问过,为0时表示未被访问;修改位:为1时表示该页面自从被装入内存后被修改过,为0时表示未修改过。 置换页面时,最先置换访问位和修改位为00的,其次是01(没被访问但被修改过)的,之后是10(被访问了但没被修改过),最后是11。 2.内聚的类型 功能内聚:完成一个单一功能,各个部分协同工作,缺一不可。 顺序内聚:

Thymeleaf:生成静态文件及异常处理java.lang.NoClassDefFoundError: ognl/PropertyAccessor

我们需要引入包: <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId></dependency><dependency><groupId>org.springframework</groupId><artifactId>sp