基础系列(6)—— C#类和对象
一、类介绍
类(class)是C#类型中最基础的类型。类是一个数据结构,将状态(字段)和行为(方法和其他函数成员)组合在一个单元中。类提供了用于动态创建类实例的定义,也就是对象(object)。类支持继承(inheritance)和多态(polymorphism),即派生类能够扩展和特殊化基类的机制。
使用类声明可以创建新的类。类声明以一个声明头开始,其组成方式如下:先是指定类的特性和修饰符,后跟类的名字,基类(如果有的话)的名字,以及被该类实现的接口名。声明头后面就是类体了,它由一组包含在大括号({})中的成员声明组成。
下面是一个名为Point的简单类的声明:
public class Point
{
public int x, y;
public Point(int x, int y){
this.x = x;
this.y = y;
}
}
使用new运算符创建类的实例,它将为新实例分配内存,调用构造函数初始化实例,并且返回对该实例的引用。下面的语句创建两个Point对象,并且将那些对象的引用保存到两个变量中:
Point p1 = new Point(, ); Point p2 = new Point(, );
当不再使用对象时,该对象所占的内存将被自动回收。在C#中,没有必要也不可能显式地释放对象。
二、类成员
类的成员或者是静态成员(static member),或者是实例成员(instance member)。静态成员属于类,实例成员属于对象(类的实例)。
下表提供了类所能包含的各种成员的描述。
成 员 |
描 述 |
常数 |
与类关联的常量值 |
字段 |
类的变量 |
方法 |
能够被类执行的计算和行为 |
属性 |
使对象能够读取和写入类的命名属性 |
索引器 |
使对象能够用与数组相同的方式进行索引 |
事件 |
能够被类产生的通知 |
运算符 |
类支持的转换和表达式运算符 |
构造函数 |
初始化类的实例或者类本身 |
析构函数 |
在永久销毁类的实例之前执行的行为 |
类型 |
被类声明的嵌套类型 |
三、可访问性
类的每个成员都有关联的可访问性,它控制能够访问该成员的程序文本区域。有5种可能的可访问性形式。表1.7概述了类的可访问性的意义。
可访问性 |
意 义 |
public |
访问不受限制 |
protected |
访问仅限于包含类或从包含类派生的类型 |
internal |
访问仅限于当前程序集 |
protected internal |
访问仅限于从包含类派生的当前程序集或类型 |
private |
访问仅限于包含类 |
四、基类
类的声明可能通过在类名后加上冒号和基类的名字来指定一个基类译注4。省略基类等同于直接从object类派生。在下面的示例中,Point3D的基类是Point,而Point的基类是object:
public class Point { public int x, y; public Point(int x, int y){ this.x = x; this.y = y; } } public class Point3D: Point { public int z; public Point3D(int x, int y, int z): Point(x, y){ this.z = z; } }
Point3D类继承了其基类的成员。继承意味着类将隐式地包含其基类的所有成员(除了基类的构造函数)。派生类能够在继承基类的基础上增加新的成员,但是它不能移除继承成员的定义。在前面的示例中,Point3D类从Point类中继承了x字段和y字段,并且每一个Point3D实例都包含三个字段x,y和z。
从类类型到它的任何基类类型都存在隐式的转换。并且,类类型的变量能够引用该类的实例,或者任何派生类的实例。例如,对于前面给定的类声明,Point类型的变量能够引用Point实例或者Point3D实例:
Point a = new Point(, ); Point b = new Point3D(, , );
五、字段
字段是与对象或类相关联的变量。当一个字段声明中含有static修饰符时,由该声明引入的字段为静态字段(static field)。它只标识了一个存储位置。不管创建了多少个类实例,静态字段都只会有一个副本。
当一个字段声明中不含有static修饰符时,由该声明引入的字段为实例字段(instance field)。类的每个实例都包含了该类的所有实例字段的一个单独副本。
在下面的示例中,Color类的每个实例都有r,g,b实例字段的不同副本,但是Black,White,Red,Green和Blue等静态字段只有一个副本:
public class Color
{ public static readonly Color Black = new Color(, , ); public static readonly Color White = new Color(, , ); public static readonly Color Red = new Color(, , ); public static readonly Color Green = new Color(, , ); public static readonly Color Blue = new Color(, , ); private byte r, g, b;
public Color(byte r, byte g, byte b) { this.r = r; this.g = g; this.b = b;
}
}
如前面的示例所示,通过readonly修饰符声明只读字段。给readonly字段的赋值只能作为声明的组成部分出现,或者在同一类中的实例构造函数或静态构造函数中出现。
六、方法
方法(method)是一种用于实现可以由对象或类执行的计算或操作的成员。静态方法(static method)只能通过类来访问。实例方法(instance method)则要通过类的实例访问。
方法有一个参数(parameter)列表(可能为空),表示传递给方法的值或者引用;方法还有返回类型(return type),用于指定由该方法计算和返回的值的类型。如果方法不返回一个值,则它的返回类型为void。
在声明方法的类中,该方法的签名必须是惟一的。方法的签名由它的名称、参数的数目、每个参数的修饰符和类型组成。返回类型不是方法签名的组成部分。
(一)参数
参数用于将值或者引用变量传递给方法。当方法被调用时,方法的参数译注5从指定的自变量(argument)译注6得到它们实际的值。C#有4种参数:值参数、引用参数、输出参数和参数数组。
值参数(value parameter)用于输入参数的传递。值参数相当于一个局部变量,它的初始值是从为该参数所传递的自变量获得的。对值参数的修改不会影响所传递的自变量。
引用参数(reference parameter)用于输入和输出参数的传递。用于引用参数的自变量必须是一个变量,并且在方法执行期间,引用参数和作为自变量的变量所表示的是同一个存储位置。引用参数用ref修饰符声明。下面的示例展示了ref参数的使用:
using System; class Test { static void Swap(ref int x, ref int y) { int temp = x; x = y; y = temp; } static void Main() { int i = , j = ; Swap(ref i, ref j); Console.WriteLine("{0} {1}", i, j); //输出 "2 1" } }
输出参数(output parameter)用于输出参数的传递。输出参数类似于引用参数,不同之处在于调用方提供的自变量初始值无关紧要。输出参数用out修饰符声明。下面的示例展示了out参数的使用:
using System;
class Test {
static void Divide(int x, int y, out int result, out int remainder) {
result = x / y;
remainder = x % y;
} static void Main() {
int res, rem;
Divide(, , out res, out rem);
Console.WriteLine("{0} {1}", res, rem); //输出 "3 1"
}
}
参数数组(parameter array)允许将可变长度的自变量列表传递给方法。参数数组用params修饰符声明。只有方法的最后一个参数能够被声明为参数数组,而且它必须是一维数组类型。System.Console类的Write和WriteLine方法是参数数组应用的很好的例子。它们的声明形式如下:
public class Console
{
public static void Write(string fmt, params object[] args) {...}
public static void WriteLine(string fmt, params object[] args) {...}
...
}
在方法中使用参数数组时,参数数组表现得就像常规的数组类型参数一样。然而,带数组参数的方法调用中,既可以传递参数数组类型的单个自变量,也可以传递参数数组的元素类型的若干自变量。对于后者的情形,数组实例将自动被创建,并且通过给定的自变量初始化。示例:
Console.WriteLine("x={0} y={1} z={2}", x, y, z);
等价于下面的语句:
object[] args = new object[]; args[] = x; args[] = y; args[] = z; Console.WriteLine("x={0} y={1} z={2}", args);
(二)方法体和局部变量
方法体指定方法调用时所要执行的语句。方法体能够声明特定于该方法调用的变量。这样的变量被称为局部变量(local variable)。局部变量声明指定类型名、变量名,可能还有初始值。下面的示例声明了一个局部变量i,其初始值为0;另一个局部变量j没有初始值。
using System;
class Squares
{
static void Main() {
int i = ;
int j;
while(i < ){
j = i * i;
Console.WriteLine("{0} x {0} = {1}", i, j);
i = i + ;
}
}
}
C#要求局部变量在其值被获得之前明确赋值(definitely)。例如,假设前面的变量i的声明没有包含初始值,那么,在接下来对i的使用将导致编译器报告错误,原因就是i在程序中没有明确赋值。
方法能够使用return语句将控制返回给它的调用方。如果方法是void的,则return语句不能指定表达式;如果方法是非void的,则return语句必须包含表达式,用于计算返回值。
(三)静态方法和实例方法
若一个方法声明中含有static修饰符,则称该方法为静态方法(static method)。静态方法不对特定实例进行操作,只能访问静态成员。若一个方法声明中没有static修饰符,则称该方法为实例方法(instance method)。实例方法对特定实例进行操作,既能够访问静态成员,也能够访问实例成员。在调用实例方法的实例上,可以用 this来访问该实例,而在静态方法中引用this是错误的。
下面的Entity类具有静态和实例两种成员:
class Entity
{
static int nextSerialNo;
int serialNo;
public Entity() {
serialNo = nextSerialNo++;
} public int GetSerialNo() {
return serialNo;
} public static int GetNextSerialNo() {
return nextSerialNo;
} public static void SetNextSerialNo(int value) {
nextSerialNo = value;
} }
每一个Entity实例包含一个序列号(并且假定这里省略了一些其他信息)。Entity构造函数(类似于实例方法)用下一个有效的序列号初始化新的实例。因为构造函数是一个实例成员,所以,它既可以访问serialNo实例字段,也可以访问nextSerialNo静态字段。
GetNextSerialNo和SetNextSerialNo静态方法能够访问nextSerialNo静态字段,但是如果访问serialNo实例字段就会产生错误。
下面的示例展示了Entity类的使用:
using System;
class Test
{
static void Main() { Entity.SetNextSerialNo(); Entity e1 = new Entity(); Entity e2 = new Entity(); Console.WriteLine(e1.GetSerialNo()); //输出 "1000" Console.WriteLine(e2.GetSerialNo()); //输出 "1001" Console.WriteLine(Entity.GetNextSerialNo()); //输出 "1002" } }
注意,SetNextSerialNo和GetNextSerialNo静态方法通过类调用,而GetSerialNo实例成员则通过类的实例调用。
(四) 虚拟方法、重写方法和抽象方法
若一个实例方法的声明中含有virtual修饰符,则称该方法为虚拟方法(virtual method)。若其中没有virtual修饰符,则称该方法为非虚拟方法(nonvirtual method)。
在一个虚拟方法调用中,该调用所涉及的实例的运行时类型(runtime type)确定了要被调用的究竟是该方法的哪一个实现。在非虚拟方法调用中,实例的编译时类型(compile-time type)是决定性因素。
虚拟方法可以由派生类重写(override)译注7实现。当一个实例方法声明中含有override修饰符时,该方法将重写所继承的相同签名的虚拟方法。虚拟方法声明用于引入新方法,而重写方法声明则用于使现有的继承虚拟方法专用化(通过提供该方法的新实现)。
抽象(abstract)方法是没有实现的虚拟方法。抽象方法的声明是通过abstract修饰符实现的,并且只允许在抽象类中使用抽象方法声明。非抽象类的派生类需要重写抽象方法。
下面的示例声明了一个抽象类Expression,它表示一个表达式树的节点;它有三个派生类Constant,VariableReference,Operation,它们实现了常数、变量引用和算术运算的表达式树节点。
using System; using System.Collections; public abstract class Expression { public abstract double Evaluate(Hashtable vars); } public class Constant: Expression { double value; public Constant(double value) { this.value = value; } public override double Evaluate(Hashtable vars) { return value; } } public class VariableReference: Expression { string name; public VariableReference(string name) { this.name = name; } public override double Evaluate(Hashtable vars) { object value = vars[name]; if (value == null) { throw new Exception("Unknown variable: " + name); } return Convert.ToDouble(value); } } public class Operation: Expression { Expression left; char op; Expression right; public Operation(Expression left, char op, Expression right) { this.left = left; this.op = op; this.right = right; } public override double Evaluate(Hashtable vars) { double x = left.Evaluate(vars); double y = right.Evaluate(vars); switch(op) { case '+' : return x + y; case '-' : return x - y; case '*' : return x * y; case '/' : return x / y; } throw new Exception("Unknown operator"); } }
前面的4个类用于模型化算术表达式。例如,使用这些类的实例,表达式x+3能够被表示为如下的形式:
Expression e = new Operation(
new VariableReference("x"), '+', new Constant());
Expression实例的Evaluate方法将被调用,以计算表达式的值,从而产生一个double值。该方法取得一个包含变量名(输入的键)和值(输入的值)的Hashtable作为其自变量。Evaluate方法是虚拟的抽象方法,意味着派生类必须重写它并提供实际的实现。
Evaluate方法的Constant的实现只是返回保存的常数。VariableReference的实现在Hashtable中查找变量名,并且返回相应的值。Operation的实现则首先计算左操作数和右操作数的值(通过递归调用Evaluate方法),然后执行给定的算术运算。
下面的程序使用Expression类,对于不同的x和y的值,计算表达式x*(y+2)。
using System; using System.Collections; class Test { static void Main() { Expression e = new Operation( new VariableReference("x"), '*', new Operation( new VariableReference("y"), '+', new Constant() ) ); Hashtable vars = new Hashtable(); Vars["x"] = ; Vars["y"] = ; Console.WriteLine(e.Evaluate(vars)); //输出 "21" Vars["x"] = 1.5; Vars["y"] = ; Console.WriteLine(e.Evaluate(vars)); //输出 "16.5" } }
(五)方法重载
方法重载(Method overloading)允许在同一个类中采用同一个名称声明多个方法,条件是它们的签名是惟一的。当编译一个重载方法的调用时,编译器采用重载决策(overload resolution)确定应调用的方法。重载决策找到最佳匹配自变量的方法,或者在没有找到最佳匹配的方法时报告错误信息。下面的示例展示了重载决策工作机制。在Main方法中每一个调用的注释说明了实际被调用的方法。
class Test { static void F() { Console.WriteLine("F()"); } static void F(object x) { Console.WriteLine("F(object)"); } static void F(int x) { Console.WriteLine("F(int)"); } static void F(double x) { Console.WriteLine("F(double)"); } static void F(double x, dpuble y) { Console.WriteLine("F(double, double)"); } static void Main(){ F(); //调用F() F(); //调用F(int) F(1.0); //调用F(double) F("abc"); //调用F(object) F((double)); //调用F(double) F((object)); //调用F(object) F(, ); //调用F(double, double) } }
如上例所示,总是通过自变量到参数类型的显式的类型转换,来选择特定方法。
(六) 其他函数成员
类的函数成员(function member)是包含可执行语句的成员。前面部分所描述的方法是主要的函数成员。这一节讨论其他几种C#支持的函数成员:构造函数、属性、索引器、事件、运算符、析构函数。
public class List { |
|
const int defaultCapacity = 4; |
常数 |
object[] items; int count; |
字段 |
public List(): this(defaultCapacity) {} public List(int capacity) { items = new object[capacity]; } |
构造函数 |
public int Count { get { return count; } } public string Capacity { get { return items.Length; } set { if (value < count) value = count; if (value != items.Length) { object[] newItems = new object[value]; Array.Copy(items, 0, newItems, 0, count); items = newItems; } } } |
属性 |
public object this[int index] { get { return items[index]; } set { items[index] = value; OnListChange(); } } |
索引器 |
public void Add(object item) { if (count == Capacity) Capacity = count * 2; items[count] = item; count++; OnChanged(); } protected virtual void OnChanged() { if (Changed != null) Changed(this, EventArgs.Empty); } public override bool Equals(object other) { return Equals (this,other as List ); } static bool Equals ( List a,List b) { if (a == null) return b == null; if (b == null || a.count != b.count) return false; for (int i = 0; i < a.count; i++) { if (!object.Equals(a.item[i], b.item[i])) { return false; } } } |
方法 |
public event EventHandler Changed; |
事件 |
public static bool operator ==(List a, List b) { return Equals(a, b); } public static bool operator !=(List a, List b) { return !Equals(a, b); } |
运算符 |
} |
(七) 构造函数
C#既支持实例构造函数,也支持静态构造函数。实例构造函数(instance constructor)是实现初始化类实例所需操作的成员。静态构造函数(static constructor)是一种在类首次加载时用于实现初始化类本身所需操作的成员。
构造函数的声明如同方法一样,不过,它没有返回类型,它的名字与包含它的类名一样。若构造函数的声明中包含static修饰符,则它声明了一个静态构造函数,否则声明实例构造函数。
实例构造函数能够被重载。例如,List声明了两个实例构造函数,一个不带参数,一个带有一个int参数。使用new运算符可以调用实例参数。下面的语句使用各个List类的构造函数创建了两个List实例。
List list1 = new List(); List list2 = new List(10);
实例构造函数不同于其他方法,它是不能被继承的。并且,一个类除了自己声明的实例构造函数外,不可能有其他的实例构造函数。如果一个类没有声明任何实例构造函数,则会自动地为它提供一个默认的空的实例构造函数。
八 、方法的参数类型
(一) 两类参数的定义:
实参:具有实际意义的参数,一般在调用方法时,在方法的括号里面传递的参数为实参
形参:在方法声明时,方法名后面括号里面的参数为形参,一般(值传递)形参是接收实参传过来的值
(二) out参数:
1、定义:
在一个方法需要返回多个参数的时候,一般在参数列表里面声明out类型的参数,以便输出多个返回值。其中,out参数只负责把结果输出,不负责输入参数。一般out声明的变量需要在方法体内部赋值
2、例子:
class Program
{
static void Main(string[] args)
{
int[] ShuZi = { 1,2,3,4,5,6};
int max1;
int min1;
int sum1;
JiSuan(ShuZi,out max1,out min1,out sum1);
Console.WriteLine("数组的最大值为:{0},数组的最小值为:{1},数组的和为:{2}",max1,min1,sum1);
Console.ReadKey();
}
public static void JiSuan(int []number,out int max,out int min,out int sum)
{
max = number[0];
min = number[0];
sum = 0;
for (int i = 0; i < 6; i++)
{
sum += number[i];
if (number[i] > max)
{
max = number[i];
}
else
{
min=number[i];
}
}
}
}
输出结果为:最大值:6,最小值:1,和为21
3 注意
out指定的参数必须在函数定义的时候就赋初值。否则则出现错误。对比ref指定的参数则可以不在函数内部进行赋初值,在函数调用时候再赋初值也可以。
(二)ref参数
1、定义:
1 class Program
2 {
3 static void Main(string[] args)
4 {
5 Program pg = new Program();
6 int x = 10;
7 int y = 20;
8 pg.GetValue(ref x, ref y);
9 Console.WriteLine("x={0},y={1}", x, y);
10
11 Console.ReadLine();
12
13 }
14
15 public void GetValue(ref int x, ref int y)
16 {
17 x = 521;
18 y = 520;
19 }
20 }
:
代码②:
1 class Program
2 {
3 static void Main(string[] args)
4 {
5 Program pg = new Program();
6 int x = 10;
7 int y = 20;
8 pg.GetValue(ref x, ref y);
9 Console.WriteLine("x={0},y={1}", x, y);
10
11 Console.ReadLine();
12
13 }
14
15 public void GetValue(ref int x, ref int y)
16 {
17 x = 1000;
18 y = 1;
19 }
20 }
由代码① 和②的运行结果可以看出,在方法中对参数所做的任何更改都将反映在该变量中,而在main函数中对参数的赋值却没有起到作用,这是不是说明不需要进行初始化呢?来看第二点
1 class Program
2 {
3 static void Main(string[] args)
4 {
5 Program pg = new Program();
6 int x;
7 int y; //此处x,y没有进行初始化,则编译不通过。
8 pg.GetValue(ref x, ref y);
9 Console.WriteLine("x={0},y={1}", x, y);
10
11 Console.ReadLine();
12
13 }
14
15 public void GetValue(ref int x, ref int y)
16 {
17 x = 1000;
18 y = 1;
19 }
20 }
出现的错误为:使用了未赋值的局部变量“x”,“y”。故可以说明ref指定的参数无论在函数定义的时候有没有赋予初值,在使用的时候必须初始化。
(四)总结
参考资料:《c#图解教程》
基础系列(6)—— C#类和对象的更多相关文章
- JAVA基础第三章-类与对象、抽象类、接口
业内经常说的一句话是不要重复造轮子,但是有时候,只有自己造一个轮子了,才会深刻明白什么样的轮子适合山路,什么样的轮子适合平地! 我将会持续更新java基础知识,欢迎关注. 往期章节: JAVA基础第一 ...
- 前端学PHP之面向对象系列第一篇——类和对象
× 目录 [1]类 [2]成员属性[3]成员方法[4]对象[5]成员访问[6]this 前面的话 面向对象程序设计(OOP)是一种计算机编程架构.计算机程序由单个能够起到子程序作用的单元或对象组成,为 ...
- C#基础篇--面向对象(类与对象)
1.类是什么? 类就相当于模板,就是把同一类的事物的共同特征进行的抽象. 类的创建和说明: 类是先根据一些具体的对象(实体的东西)来抽象出来的共同的特性,然后用代码来表示. 在类中,用数据表示事物的 ...
- Kotlin基础(三)类、对象和接口
类.对象和接口 一.定义类的继承结构 一)Kotlin中的接口 Kotlin的接口与Java8中相似,它们可以包含抽象方法的定义以及非抽象方法的实现,但它们不能包含任何状态. interface Cl ...
- Java入门系列-14-深入类和对象
这篇文章用大量的代码帮你搞懂:值传递和引用传递.构造方法.方法重载.static关键字的使用 方法参数传递-值传递和引用传递 1.值传递 敲一敲: /** * 使用方法交换两个变量的值 * @auth ...
- Scala 基础(4)—— 类和对象
1. 类.字段和方法 Scala 用 class 关键字定义类,一旦定义好一个类,就可以使用 new 关键字创建对象. Scala 使用 new 调用无参构造器时,可以不使用 (),推荐不使用括号: ...
- Java学习日记基础(五)——类、对象之this、静态变量(类变量)、静态方法(类方法)、四大特征
this 赵本山问奥尼尔:“我的爸爸的爸爸是谁?” 奥尼尔:“不知道” 赵本山:“你傻啊,是我爷爷” 奥尼尔回去问科比:“我的爸爸的爸爸是谁?” 科比:“不知道” 奥尼尔:”你傻啊,是赵本山的爷爷“ ...
- Java知识系统回顾整理01基础02面向对象01类和对象
一.面向对象实例--设计英雄这个类 LOL有很多英雄,比如盲僧,团战可以输,提莫必须死,盖伦,琴女 所有这些英雄,都有一些共同的状态 比如,他们都有名字,hp,护甲,移动速度等等 这样我们就可以设计一 ...
- JAVA基础系列:Object类
1. 万物皆对象 1. JVM在编译源代码时,在遇到没有继承Object的对象的时候,编译器会默认指定一个默认的父类Object 2. Object 和接口的关系,接口是否继承Object?接口没有继 ...
- JAVA 类和对象基础知识详解
/*文章中用到的代码只是一部分,需要源码的可通过邮箱联系我 1978702969@qq.com*/ 和C++一样,JAVA也是一门面向对象的语言,其基础和核心是类和对象.而面向对象的思想是来源与显示生 ...
随机推荐
- 关于IScroll使用中的常见问题与解决方案
1.在iscroll4的滚动容器范围内,点击input框.select等表单元素时没有响应这个问题原因在于iscroll需要一直监听用户的touch操作,以便灵敏的做出对应效果,所以它把其余的默认事件 ...
- vue axios拦截器的封装
// request.js import axios from 'axios' import qs from 'qs' // 创建axios实例 const service = axios.creat ...
- restframework序列化使用方法
serializers.Serializer class Userinfoserializers(serializers.Serializer): username = serializers.Cha ...
- Java操作Redis(一)
一.创建项目,导入依赖 <dependency> <groupId>redis.clients</groupId> <artifactId>jedis& ...
- [译]C语言实现一个简易的Hash table(6)
上一章中,我们实现了Hash表中的插入.搜索和删除接口,我们在初始化hash表时固定了大小为53,为了方便扩展,本章将介绍如何修改hash表的大小. 设置Hash表大小 现在,我们的hash表是固定大 ...
- 2018年数学建模国赛B题 智能RGV的动态调度策略
第一种情况大致思路: 每秒判断各个CNC的状态,若工作完成或者是出于空闲状态下则向RGV发出一个请求.同时,RGV每秒判断自己的状态(上下料.移动.闲置.清洗等),如果是处于闲置状态,则启用调度算法, ...
- 基于visual studio 2017 以及cubemx 搭建stm32的开发环境(0)
(1)安装visual studio 2017 官网下载安装即可 (2)安装visual GDB 链接:https://pan.baidu.com/s/1TgXI1BRQLAWiWlqCcIS9TA ...
- 20155227 《Java程序设计》实验五 Java网络编程及安全实验报告
20155227 <Java程序设计>实验五 Java网络编程及安全实验报告 实验内容 任务一: 编写MyBC.java实现中缀表达式转后缀表达式的功能. 编写MyDC.java实现从上面 ...
- 1.Delphi Rest后台+MUI前台开发App前言
尽管不是专业的程序猿,但是对Delphi的喜爱已经10多年了.一直以来用Delphi开发一些小应用若干个,同时用Delphi给朋友开发一些中小型的业务平台也有几个.可以说Delphi对于数据库的操作, ...
- Swing 解决 idea 找不到创建gui form的问题
果然,寄希望于百度google不如自己动手,还是得吃透文档, 然后就是对于别人的博客要严格对照步骤来,否则都容易达不到效果 这边gui form在idea下找不到创建,百度google一个说的也没有, ...