Graphics DrawString参数无效

Go 旅城通票 2013-08-28 11:46:36
加精
一个生成图片的ashx页面,执行到DrawString时就报错,重启iis后第一次或者第二次访问能正常生成图片,后续的访问执行到DrawString时就报错了。什么问题?

framework版本是2.0的,系统win2003

[ArgumentException: 参数无效。]
System.Drawing.Graphics.CheckErrorStatus(Int32 status) +1048569
System.Drawing.Graphics.DrawString(String s, Font font, Brush brush, RectangleF layoutRectangle, StringFormat format) +211
System.Drawing.Graphics.DrawString(String s, Font font, Brush brush, Single x, Single y) +53
System.Web.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +406
System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +76

public void ProcessRequest(HttpContext context)
{ Bitmap nbitmap = new Bitmap(1000, 1000);
Graphics g = Graphics.FromImage(nbitmap);

Font font = new Font(FontFamily.GenericSansSerif, 10, FontStyle.Italic);
Brush brush = Brushes.Black;
g.DrawString("测试字符串", font, brush, 10, 10);/////
g.Dispose();
font.Dispose();
brush.Dispose();
nbitmap.Save(context.Server.MapPath("~/upload/cert" + DateTime.Now.ToString("yyyyMMddHHmmssfff") + ".jpg"));
nbitmap.Dispose();
}
...全文
3304 12 打赏 收藏 转发到动态 举报
写回复
用AI写文章
12 条回复
切换为时间正序
请发表友善的回复…
发表回复
nbh_king 2015-10-26
  • 打赏
  • 举报
回复
每天回帖即可获得10分可用分
EnForGrass 2013-08-28
  • 打赏
  • 举报
回复
引用 1 楼 Chinajiyong 的回复:
对象释放的顺序有没有问题呢
Bitmap nbitmap = new Bitmap(1000, 1000); Graphics g = Graphics.FromImage(nbitmap); Font font = new Font(FontFamily.GenericSansSerif, 10, FontStyle.Italic); Brush brush = Brushes.Black; g.DrawString("测试字符串", font, brush, 10, 10);///// font.Dispose(); brush.Dispose(); g.Dispose(); //font.Dispose(),brush.Dispose()删除试试 nbitmap.Save(context.Server.MapPath("~/upload/cert" + DateTime.Now.ToString("yyyyMMddHHmmssfff") + ".jpg")); nbitmap.Dispose();
EnForGrass 2013-08-28
  • 打赏
  • 举报
回复
对象释放的顺序有没有问题呢
jshi123 2013-08-28
  • 打赏
  • 举报
回复
Brushes.Black是静态对象,Dispose释放对应的HBrush句柄,以后再调用就出错了。 从报错信息看也很明显,这是DrawString方法的最后两行: int status = SafeNativeMethods.Gdip.GdipDrawString(new HandleRef(this, this.NativeGraphics), s, s.Length, new HandleRef(font, font.NativeFont), ref layoutRect, new HandleRef(format, handle), new HandleRef(brush, brush.NativeBrush)); this.CheckErrorStatus(status); 前一句调用native gdi函数去执行,后一句检查win32返回状态码。 在CheckErrorStatus方法中,如果status != 0,则: throw SafeNativeMethods.Gdip.StatusException(status); 把error status转成相应的各类Excetion 其中: case 2: return new ArgumentException(SR.GetString("GdiplusInvalidParameter")); 你看到的就是ArgumentException,对应win32 error code是2 表示传到GdipDrawString函数中的某个参数是无效的。
Go 旅城通票 2013-08-28
  • 打赏
  • 举报
回复
引用 7 楼 guwei4037 的回复:
测试过有效的代码:
protected void Page_Load(object sender, EventArgs e)
        {
            if (!Page.IsPostBack)
            {
                try
                {
                    string str = Server.MapPath("~/upload/test.jpg");
                    System.Drawing.Image myImage = System.Drawing.Image.FromFile(str);
                    Bitmap map = new Bitmap(myImage);
                    myImage.Dispose();
                    Graphics graphics = Graphics.FromImage(map);
                    graphics.InterpolationMode = InterpolationMode.HighQualityBilinear;
                    SolidBrush brush = new SolidBrush(Color.Red);
                    PointF P = new PointF(100, 100);
                    Font font = new Font("宋体", 40);
                    graphics.DrawString("guwei4037", font, brush, P);
                    map.Save(str.Substring(0, str.LastIndexOf("\\") + 1) + "new" + str.Substring(str.LastIndexOf("\\") + 1, str.LastIndexOf(".") - str.LastIndexOf("\\") - 1) + str.Substring(str.LastIndexOf("."), str.Length - str.LastIndexOf(".")), ImageFormat.Jpeg);
                    font.Dispose();
                    graphics.Dispose();
                }
                catch { }
            }
        }
看来真是画笔的问题。。
tan598121925 2013-08-28
  • 打赏
  • 举报
回复
学习了
Go 旅城通票 2013-08-28
  • 打赏
  • 举报
回复
nnd。。。。
// Brush brush = Brushes.Black;
SolidBrush brush = new SolidBrush(Color.Black);
改成Brush的子类就没问题了,搞什么飞机。。。ms的bug吗?谁能给解释下什么问题噶。。。 等等看有木有人有其他的观点,晚点结贴。。
全栈极简 2013-08-28
  • 打赏
  • 举报
回复
测试过有效的代码:
protected void Page_Load(object sender, EventArgs e)
        {
            if (!Page.IsPostBack)
            {
                try
                {
                    string str = Server.MapPath("~/upload/test.jpg");
                    System.Drawing.Image myImage = System.Drawing.Image.FromFile(str);
                    Bitmap map = new Bitmap(myImage);
                    myImage.Dispose();
                    Graphics graphics = Graphics.FromImage(map);
                    graphics.InterpolationMode = InterpolationMode.HighQualityBilinear;
                    SolidBrush brush = new SolidBrush(Color.Red);
                    PointF P = new PointF(100, 100);
                    Font font = new Font("宋体", 40);
                    graphics.DrawString("guwei4037", font, brush, P);
                    map.Save(str.Substring(0, str.LastIndexOf("\\") + 1) + "new" + str.Substring(str.LastIndexOf("\\") + 1, str.LastIndexOf(".") - str.LastIndexOf("\\") - 1) + str.Substring(str.LastIndexOf("."), str.Length - str.LastIndexOf(".")), ImageFormat.Jpeg);
                    font.Dispose();
                    graphics.Dispose();
                }
                catch { }
            }
        }
Go 旅城通票 2013-08-28
  • 打赏
  • 举报
回复
引用 3 楼 jshi123 的回复:
静态刷子Brushes.Black不需要Dispose吧。
应该不是这个问题。。刚才测试注释掉 font.Dispose();也只能重启iis才能生成图片,并且是第一次,第二次访问没报错,后续的访问还是到DrawString那不就参数错误了 大家新建ashx页面运行测试下。我这里就是这个问题,不知道你们那边出这种问题吗 或者哪个能提供一个向图片中写入文字的demo,我这边测试没问题就结贴了
Go 旅城通票 2013-08-28
  • 打赏
  • 举报
回复
引用 2 楼 Chinajiyong 的回复:
[quote=引用 1 楼 Chinajiyong 的回复:] 对象释放的顺序有没有问题呢
Bitmap nbitmap = new Bitmap(1000, 1000); Graphics g = Graphics.FromImage(nbitmap); Font font = new Font(FontFamily.GenericSansSerif, 10, FontStyle.Italic); Brush brush = Brushes.Black; g.DrawString("测试字符串", font, brush, 10, 10);///// font.Dispose(); brush.Dispose(); g.Dispose(); //font.Dispose(),brush.Dispose()删除试试 nbitmap.Save(context.Server.MapPath("~/upload/cert" + DateTime.Now.ToString("yyyyMMddHHmmssfff") + ".jpg")); nbitmap.Dispose();[/quote] 注释掉了还是不行
jshi123 2013-08-28
  • 打赏
  • 举报
回复
静态刷子Brushes.Black不需要Dispose吧。
面向对象编程的基础 要了解面向对象编程(OOP)的基本概念,需要理解 OOP 的三个主要概念,它们撑起 了整个 OOP 的框架。这三个概念是:封装、继承性和多态性。除此以外,还需了解对象、 类、消息、接口、及抽象等概念。 2.2.1 对象 现实世界中的对象具两个特征:状态和行为。例如:自行车有状态(传动装置、步度、 两个车轮和齿轮的数目等)和行为(刹车、加速、减速和换档等)。 其次,我们再来看看软件对象。软件对象是现实世界对象的模式化产物,他们也有状态 和行为。软件对象把状态用数据表示并存放在变量里,而行为则用方法实现。实际上,软件 对象还包括了数据结构和使用这些数据结构的代码。因此也可以说:软件对象是现实世界客 观事务的软件化模拟,是变量(数据和数据结构)和相关方法(对数据操作和对象管理的程 序)的软件组合。 在面向对象的程序设计中,你可以用软件对象表示现实世界的对象,而这些软件对象和 现实世界对象是相对应的。例如:如果你正在建立一个帐户管理系统,那么你的对象就是帐 户、欠款、信用卡、月收入、贷款、交易等等。如果你设计一个电子实习交通工具系统,那 么你的对象就是汽车、摩托车、自行车等等。就自行车的软件对象而言,表示该对象的状态 和行为应为与变量和方法相对应。自行车的状态:数度是 10mp(每小时 10 米),步度是 90rpm (每分钟 90 转),当前传动装置是第 5 个齿轮。再面向对象的程序设计中,这些数据应放在 变量中。自行车的行为:刹车,改变步度和换档。在面向对象的程序设计中,这些行为用方 法实现。 在 OOP 技术中,对象充当了一个很重要的角色。对象的数据是组成对象的核心,而方法 则环绕这个核心并隐藏在对象之中。 2.2.2 封装 "封装"是 OOP 语言的优点之一。把一个对象的数据加以包装并置于其方法的保护之下 称为封装。所谓封装就是对数据的隐藏。封装实现了把数据和操作这些数据的代码包装成为 一个对象(即离散的部件),而数据和操作细节(方法)隐藏起来。如果增加某些限制,使 得对数据的访问可按照统一的方式进行,那些能比较容易地产生更为强壮的代码。 OOP 语言提出一种(或称为协议),以保证对数据进行统一的操作。通常的做法是:程 序和对象数据的交互作用通过一个公开的接口进行,而不直接进行操作。由于把数据封装在 对象中,所以,访问对象中的数据只有一种途径,那就是利用一个公开的接口。 实际上,封装在程序和数据之间设置了一道栅栏,它可以阻止一部分的设计错误,不至 于涉足应用程序其他部分的数据。 2.2.3 消息 一个单独的对象一般不十分有用,而作为一员出现在包含有许多其他对象的大程序或应 用程序之中,通过这些对象的相互作用,程序员可实现高层次的操作和更负责的功能。某此 对象通过向其他对象发送消息与其他对象进行交互作用和通信。 消息是以参数的形式传递给某方法的。一个消息通常由三部分组成: 1. 消息传送到对象的名称。 2. 要执行的方法的名称。 3. 方法需要的任意参数。 2.2.4 类 类是一个蓝图或样板,定义了某种类型的所有对象的变量和方法。 在 java 语言中,Java 程序的基本单位是类,也就是说:一个 Java 程序是由多个类组成 的。定义一个类与定义一个数据类型是有区别的。在程序设计语言中,把定义数据类型的能 力作为一种很重要的能力来对待。在面向对象的语言中,类的功能更强大,这是因为类不仅 含有定义数据类型的功能,而且还包含了对方法的定义。 对象实际是类中的一个实例。生成实例的过程叫做把"一个对象实例化"。一个实例化 的对象实际上是由若干个实例变量和实例方法组成的。当你创建出一个类的实例时,系统将 为实例变量指定内存,然后你就可以利用实例方法去做某些事情。 2.2.5 继承 继承是指建立子类的能力。子类继承了父亲的特征和功能。类的层次结构类似于一棵数 的结构,也像一个家庭谱系。它显示了根和它的导出类之间的关系。 子类从它先辈类那里继承了代码和数据,这样,它就可以执行先辈类的功能和访问先辈 类的数据。一个纯面向对象程序设计的语言将具有严格的继承性。 通过对象、类,我们实现了封装,通过子类我们可以实现继承。例如,公共汽车、出租 车、货车等都是汽车,但它们是不同的汽车,除了具有汽车的共性外,它们还具有自己的特 点(如不同的操作方法,不同的用途等)。这时我们可以把它们作为汽车的子类来实现,它们 继承父类(汽车)的所有状态和行为,同时增加自己的状态和行为。通过父类和子类,我们实 现了类的的层次,可以从最一般的类开始,逐步特殊化,定义一系列的子类。同时,通过继 承也实现了代码的复用,使程序的复杂性线性地增长,而不是呈几何级数增长。 2.2.6 抽象 面向对象的程序设计系统鼓励充分利用"抽象"。在现实世界中,人们正是通过抽象来 理解复杂的事务。例如:人们并没有把汽车当作成百上千的零件组成来认识,而是把它当作 具有自己特定行为的对象。人们可以忽略发动机、液压传输、刹车系统等如何工作的细节, 而习惯于把汽车当作一个整体来认识。 包含通用对象类的库叫作类库。 2.2.7 多态型 面向对象程序的最后一个概念是多态性。凭借多态性,你可以创建一个新的对象,它具 有与基对象相同的功能,但是这些功能中的一个或多个是通过不同的方式完成的。例如:在 Java 中你可以凭借多态性,通过一个画圆的对象,来创建一个画椭圆或矩形的对象。不管是 画圆,画椭圆还是画矩形的方法,它们都有一个相同的方法名,但以不同的方式完成他们的 画圆的功能。 1.8 类和对象 1.8.1 类 类是组成 Java 程序的基本要素。它封装了一类对象的状态和方法,是这一类对象的 原型。定义一个类,实际上就是指定该类所包含的数据和对数据进行操作的代码。 类通过关键字 class 来定义,一般格式为: 【类说明修饰符】class 类名【extends 子句】【implements 子句】 type instance-varable1; type instance-varable2; type instance-varable3; the methodname1(parameter-list){method-body;} the methodname2(parameter-list){method-body;} the methodnameN (parameter-list){method-body;} 下面将类定义格式的项目说明如下: (1) class 是类说明关键字。 (2) 类名是由程序员自己定义的 Java 标识符,每个类说明必须有 class 和类名。 (3) 类说明修饰符包括:  abstract 说明一个类为抽象类,抽象类是指不能直接实例化对象的类。  final 说明一个类为最终类,即改类不能再有子类。  public 说明类为公共类,该类可以被当前包以外的类和对象使用。  private 说明类为私有类。 (4) extends 子句用于说明类的直接超类。 (5) implements 子句用于说明类中将实现哪些接口,接口是 Java 的一种引用类 型。 (6) 类体包含了变量和方法。在类体中定义的数据、变量和方法称为类的成员, 或称为实例变量和实例方法。 (7) 例如: 下例定义了一个 Point 类 ,并且声明了它的两个变量 x、y 坐标 ,同时实现 init()方法 对 x、y 赋初值 。 class Ponit { int x,y; void init(int ix, int iy){ x=ix; y=iy; } } 类中所定义的变量和方法都是类的成员。对类的成员可以设定访问权限 ,来限定 其它对象对它的访问,访问权限所以有以下几种:private, protected, public, friendly。 1.8.2 对象 把类实例化,我们可以生成多个对象,这些对象通过消息传递来进行交互(消息 传递即激活指定的某个对象的方法以改变其状态或让它产生一定的行为),最终完 成复杂的任务。一个对象的生命期包括三个阶段:创建对象、对象的引用和释放对 象 。 1.8.3 创建对象 创建对象包括声明、实例化和初始化三方面的内容。通常的格式为 : 1. 声明对象 对象声明实际上是给对象命名,也称定义一个实例变量。对象声明的一般格式为: type name 其中,type 是一个类的类名,用它声明的对象将属于改类;name 是对象名。 例如: Date today; Rectangle myRectangle; 第一条语句说明了对象 today 属于 Date 类,第二条语句说明了对象 myRectangle 属于 Rectangle 类。对象说明并没有体现一个具体的对象,只有通过实例化后的对 象才能被使用。 2. 实例化对象 实例化对象就是创建一个对象。实例化对象意味着给对象分配必要的存储空间,用 来保存对象的数据和代码。实例化后的每个对象均占有自己的一块内存区域,实例 化时,每个对象分配有一个"引用"(reference)保存到一个实例变量中。"引用" 实际上是一个指针,此指针指向对象所占有的内存区域。 因此,对象名(变量)实际上存放的是一个被实例化之后的对象所占有的内存区域 的指针。 例如: type objectName = new type ( [paramlist] ); 运算符 new 为对象分配内存空间 ,实例化一个对象 。new 调用对象的构造方法,返 回对该对象的一个引用(即该对象所在的内存地址)。用 new 可以为一个类实例化, 多个不同的对象。这些对象分别占用不同的内存空间,因此改变其中一个对象的状 态不会影响其它对象的状态 。 3.初始化对象 生成对象的最后一步是执行构造方法,进行初始化。由于对构造方法可以进行重写 ,所以通过给出不同个数或类型的参数会分别调用不同的构造方法。 例子:以类 Rectangle 为例,我们生成类 Rectangle 的对象: Rectangle p1=new Rectangle (); Rectangle p2=new Rectangle (30,40); 这里,我们为类 Rectangle 生成了两个对象 p1、p2,它们分别调用不同的构造方法, p1 调用缺省的构造方法(即没有参数),p2 则调用带参数的构造方法。p1、p2 分别对 应于不同的内存空间,它们的值是不同的,可以完全独立地分别对它们进行操作。虽 然 new 运算符返回对一个对象的引用,但与 C、C++中的指针不同,对象的引用是指 向一个中间的数据结构,它存储有关数据类型的信息以及当前对象所在的堆的地址, 而对于对象所在的实际的内存地址是不可操作的,这就保证了安全性。 1.8.4 对象的引用 对象的使用包括引用对象的成员变量和方法,通过运算符·可以实现对变量的访问和方法的调 用,变量和方法可以通过设定一定的访问权限(见下面的例子)来允许或禁止其它对象对它的 访问。 我们先定义一个类 Point。 例子: class Point{ int x,y; String name = "a point"; Point(){ x = 0; y = 0; } Point( int x, int y, String name ){ this.x = x; this.y = y; this.name = name; } int getX(){ return x; } int getY(){ return y; } void move( int newX, int newY ){ x = newX; y = newY; } Point newPoint( String name ){ Point newP = new Point( -x, -y, name ); return newP; } boolean equal( int x, int y ){ if( this.x==x && this.y==y ) return true; else return false; } void print(){ System.out.println(name+" : x = "+x+" y = "+y); } } public class UsingObject{ public static void main( String args[] ){ Point p = new Point(); p.print(); //call method of an object p.move( 50, 50 ); System.out.println("** after moving **"); System.out.println("Get x and y directly"); System.out.println("x = "+p.x+" y = "+p.y); //access variabl es of an object System.out.println("or Get x and y by calling method"); System.out.println("x = "+p.getY()+" y = "+p.getY()); if( p.equal(50,50) ) System.out.println("I like this point!!!! "); else System.out.println("I hate it!!!!! "); p.newPoint( "a new point" ).print(); new Point( 10, 15, "another new point" ).print(); } } 运行结果为: C:\java UsingObject a point : x = 0 y = 0 **** after moving ***** Get x and y directly x = 50 y = 50 or Get x and y by calling method x = 50 y = 50 I like this point!!!! a new point : x = -50 y = -50 another new point : x = 10 y = 15 1.引用对象的变量 要访问对象的某个变量,其格式为: objectReference.variable 其中 objectReference 是对象的一个引用,它可以是一个已生成的对象,也可以是能够生成对 象引用的表达式。 例如:我们用 Point p=newPoint();生成了类 Point 的对象 p 后,可以用 p.x,p.y 来访问该点的 x、y 坐标,如 p.x = 10; p.y = 20; 或者用 new 生成对象的引用,然后直接访问,如: tx=new point().x; 2.调用对象的方法 要调用对象的某个方法,其格式为: objectReference.methodName ( [paramlist] ); 例 如我们要移动类 Point 的对象 p,可以用 p.move(30,20); 虽然我们可以直接访问对象的变量 p.x、p.y 来改变点 p 的坐标,但是通过方法调用的方 式来实现能更好地体现面向对象的特点,建议在可能的情况下尽可能使用方法调用。 同样,也可以用 new 生成对象的引用,然后直接调用它的方法,如 new point(). move (30,20); 前面已经讲过,在对象的方法执行完后,通常会返回指定类型的值,我们可以合法地使 用这个值,如:例子中类 Point 的方法 equal 返回布尔值,我们可以用它来作为判断条件分别执 行不同的分支。如: if (p.equal (20,30)){ ...... //statements when equal }else { ...... //statements when unequal } 另外,类 Point 的方法 newPoint 返回该点关于原点的对称点,返回值也是一个 Point 类型,我们 可以访问它的变量或调用它的方法,如: px = p.newPoint().x 或 px = p.newPoint(). getX(); 1.8.5 成员变量 对象具有状态和行为,而对象的状态则是用变量或数据来描述的。在一个类中,对象的 状态是以变量或数据的形式定义的。 例如: "盒子"的体积的状态主要是宽度、高度、和深度。因此在类定义"盒子"对象时,只 将这三个属性作为其主要的状态,并用变量的形式来描述,这些变量称为成员变量。而在对 象实例化后,这些变量称为实例变量。 1.8.6 成员变量定义格式 成员变量定义的一般格式为: 【Modifer】type variablelist; 其中, type 指定变量的类型,它可以时 Java 的任意一种类型。 variablelist 是一组逗号隔开的变量名(变量列表),每个变量都可带有自己的初始化的表达 式。 例如: xint ,z; aint ,b=2,c=3; Modifer 是定义变量的修饰符,它说明了变量的访问权限和某些使用规则。变量修饰符可以 是关键字 public、protected、private、final、static、transient 和 volatile 的组合。 1.8.7 成员变量的初始化 当成员变量含有自己的初始化表达式时,可以创建实例的方式使成员变量实例化。 例如: class Box{ double width = 10; double height= 15; double depth= } 变量 width、height、depth 是成员变量。在执行 Box myBox1 = new Box()语句之后, new 运算符就创建了一个实例,并将变量分别赋初值为 10、15、20。在此时的变量 width、 height、depth 称为实例变量。 注意:在初始化表达式中,不能包含成员变量本身或同类的其他成员变量。例如,下面 的用法式错误的: class Test{ int int t =j; int } 错误有两个:一个式变量 k 的初始化涉及对 k 自身的访问;二式对 t 进行初始化时含有 对 j 的访问,而 j 的说明在其后。 1.8.8 成员变量的访问权限 成员变量或方法的访问权限是用访问权限修饰符来指定的。Java 的访问权限修饰符包括四种 显示方式修饰符和一种隐含方式修饰符,即: 1. 公用变量 用 public 说明的变量是公有变量。 访问权限:允许任何包中的任何类的变量访问。 例如:下面的代码中,在类 Alpha 中说明了一个公用变量 i_public,而在另一个类 Beta 中可以访问该变量。 class Alpha{ public int i_public ; } class Beta{ void accessmethod() { Alphaa= newAlpha(); a.i_public=10; } } 2. 私有变量 //说明公用变量 i_public //访问公用变量 i_public 用 private 说明的变量是私有变量。 访问权限:只能被定义它的类的变量访问。 例如:下面的代码中,在类 Alpha 中说明了一个私有变量 i_private,其他类不允 许访问。 正确的访问格式: class Alpha{ 3. 保护变量 } public int i_private ; void accessmethod() { Alphaa= newAlpha(); a.i_private=10; } //说明私有变量 i_private //访问私有变量 i_private 用 protected 说明的变量是保护变量。 访问权限:允许类自身、子类以及在同一个包中的所有类的变量访问。 例如:假定某个包 Geek 中含有两个成员类 Alpha 和 Beta,若在类 Alpha 中说明 了一个保护变量 i_protected,则在另外一个类 Beta 中可以访问该变量。 class Alpha{ public int i_protected; void accessmethod() } class Beta { void accessmethod() { Alpha a= new Alpha(); a.i_protected=10; } } 4. 私有保护变量 //说明保护变量 i_protected //访问保护变量 i_protected 用 private protected 说明的变量是私有保护变量。 访问权限:允许类自身以及它的子类变量访问。 例如:下面的两种访问方式是可行的。 (1) 在类中访问私有保护变量 例如: class Alpha{ private protected int i_pri_prot ; void accessmethod() { Alphaa= newAlpha(); } } a. i_pri_prot =10; //访问私有保护变量 i_pri_prot (2) 在子类中访问私有保护变量 例如: class Alpha{ private protected int i_pri_prot=10 ; } class Beta extends Alpha { void accessmethod() { Alphaa= newAlpha(); } } a. i_pri_prot =30; //访问私有保护变量 i_pri_prot 在程序执行时,变量 i_pri_prot 的值是 30,而不是 10; 5. 友好变量 如果一个变量没有显示地设置访问权限,则该变量为友好变量。 访问权限:允许类自身以及在同一个包中地所有类地变量访问。 例如:下面的类中定义了一个友好变量: class Alpha{ int i_friendly ; void accessmethod() { Alphaa= newAlpha(); } } a. i_friendly=10; //访问友好变量 i_friendly 在了解了成员变量的访问权限之后,那么在说明每一个成员变量时,都可以按访问权限给变 量提供适当的保护措施,这样就加强了变量的安全性。 名称 公用 私有 保护 私有保护 访问权限修饰 public private protected private protected 类 √ √ √ √ 子类 √ √ √ 包 √ * 所有类 √ √ 友好 friendly √ √ 注:表中√的为可选,打*的说明有特殊限制。*号是针对子类访问保护变量而言,即一个子类只有与超类 在同一个包中,才可以访问超类对象的保护变量。 1.8.9 静态变量 用 static 说明的变量是静态变量。静态变量与其他成员变量有区别:其他成员变量必须通过 类的对象来访问,每个对象都有这些变量的备份;而静态变量独立于改类中的任何对象,它 在类的实例中只有一个备份,可以直接使用,而不必通过类的对象去访问,它一直属于定义 它的类,因此也称为类变量。类的所有对象都共享 static 变量。static 变量通常也称为全局变 量。 例如: 静态变量的定义和引用。首先在类 MyDemo 中定义了 static 变量 x,y 然后在类 MyStaticDemo 中输入变量 x 和 y 的值。 import java.awt.Graphics; class MyDemo { static int x=80; static int y=120; } class MyStaticDemo extends java.applet.Applet { public void paint(Graphics g) { g.drawString("x="+MyDemo.x+"y="+MyDemo.y,25,50); } } 程序运行结果: x=80 y=120 在上面的程序中,在访问的静态变量 x 和 y 时,是通过类名 MyDemo 直接访问的。 static 也可以说明方法。用 static 说明的方法是静态方法或类方法,在实例中只有一 个备份。该方法具有以下约束: a) b) c) 它们仅可以调用其他 static 方法。 它们仅可以访问 static 变量。 它们不能参考 this 或 super。 如果类的成员被定义为 static,则可以通过下面形式引用: 类名,成员名 这里,类名是定义 static 成员所属的类。Java 通过这样的方式,实现了全局方法和变量。 1.8.10 final 变量 用 final 说明的变量可以当作一个常量使用,不得对其进行任何修改。若某此变量为了 防止被修改,则定义变量时必须初始化一个 final 变量。在这一点上,final 与 C\C++的 const 相似。比如: final int MONDAY=1; final int TUSDAY=2; final int WEDNESDAY=3; ...... 以后程序可以把上述变量当作常量来使用,而不用担心其被修改。 final 变量用大写字母来表示,这是一种习惯约定。final 变量不占内存空间,实际上也 就是一个常数。 1.9 方法 1.9.1 方法的定义 方法也是类的一个成员,定义方法时在定义类的同时进行的。其一般格式为: type name(parameter -list) { //方法体 } 格式说明: (1) type 指定方法的返回类型,简称方法的类型,它可以是任何有效的类型, 包括类类型。方法的返回或带值返回都由 return 语句实现,当一个方法没 有返回值时,其 type 必须为 void,且 return 语句可以省略。 (2) name 指定方法名,方法名可以是合适的 Java 标识符。 (3) parameter-list 指定方法的参数列表,参数包括参数的类型和参数名,每个 参数用逗号隔开。在定义方法时,其参数将作为形参;在调用方法时,其 参数被称为实参。调用时是把实参的值传递给形参。入过方法没有参数参数列表为空,但括号"()"不能省略。 (4) 方法体包含了一组代码,它用于对数据处理。方法体用以对大括号"{}"括 起来。 例如:Box 类封装"盒子"的状态和行为,即数据变量和方法,用方法 volume 计 算 Box 对象的体积。 import java.awt.Graphics; class Box { double width; double height; double depth; void setDemo(double w,double h,double d) { width=w; height=h; depth=d; } } double volume() { return width*height*depth; } class BoxDemo extends java.applet.Applet { public void paint(Graphics g) { double d_volume; Box myBox = new Box(); myBox.setDemo(10,20,30); //调用方法 setDemo 给变量赋值 d_volumn = myBox.volume(); //计算体积 g.drawString("myBox 的体积是:"+ d_volumn,25,50); } } 程序运行的结果如下: myBox 的体积是:6000 1.9.2 方法的访问权限 方法同成员变量一样,可以在定义时说明方法的访问权限,其说明格式和访问机制与成 员变量完全一样。 例如:私有方法、私有保护方法只能被同一个类中的方法调用,公用方法可以被其他类 的方法调用。 import java.awt.Graphics; class Alpah1 { pivateint i_private() { int x=10; int y=20; return x+y; } public int i_public1() { return i_private(); } } class Alpah2 { public public int x,y; private protected int i_protected(int a,int h) { x=a; y=b; return x+y; } public void i_public2(int i,int j) { k=i_protected(i,j); } } class Test extends java.applet.Applet { public void paint(Graphics g) { int p1; Alpah1 ap1= newAlpah1(); Alpah2 ap2= newAlpah2(); p1=ap1.i_public1(); ap2. i_public2(50,50); g.drawString("i_public1()的返回值是:"+p1,25,50); g.drawString("i_public2()的返回值是:"+ap2.k,25,50); } } 程序运行的结果如下: 方法 i_public1()的返回值是:30 方法 i_public2()的返回值是:100 程序说明: (1)在类 Alpah1 中,方法 i_private()是私有的,它只能被同类的方法 i_public1()调用。 而方法 i_public1()是公有的,它可以被另一类 Demo 中的方法 paint()调用。 (2)在类 Alpah2 中,方法 i_protected()是私有保护性的,它不能被其他类的方法调用, 而只能被同类(或子类)的方法 i_public2()调用。同样,方法 i_public2()也是公有的,它可 以被另一类 Test 中的方法 paint()调用。 (3)在方法 i_public1()中,语句 return i_private()执行的顺序为:先调用,后返回。其 返回值是 x+y 的和。 (4)在方法 i_public2(int i,int j)中,将形参 i 和 j 作为调用方法中的实参。 如:k=i_protected(i,j); (5) 在类 Alpha2 中,定义了成员变量 k、x 和 y。其中 k 是公有的,它可被类 Test 中 的方法引用,其格式为:ap2.k;而变量 x 和 y 是私有的。它只能被同一类的方法引用。 (6)在类 Alpah1 方法 i_private()中,定义的变量 x 和 y 是局部变量。 局部变量作用域是:只能在定义它的方法中有效,即使同一类中的方法也不能引用。 例如:方法 i_public()是不能访问局部变量 x 和 y 的。 1.9.3 对象作为参数 例:给定两个数,按大小顺序输出。 import java.awt.Graphics; class Test1 { public int a,b; void mov(int i,int j) { a=i; b=j; } } class Test2 { void max(Test1 test1) { int c; if(test1.aGraphics g) { Test1myTest1=new Test1(); Test2 myTest2 = new Test2(); myTest1.mov(45,55); //调用 Test2 类的方法 max,并将对象 myTest1 作为实参。 myTest2.max(myTest1); g.drawString("大小顺序为:"+ myTest1.a+","+myTest1.b,25,50); } } 1.9.4 对象作为返回值 下面的例子是把对象作为返回值的编程方法。 例如:利用方法返回对象的特性,实现对实例变量的值的递增。 import java.awt.Graphics; class Test { public int a,b; void mov( int i,int j) { a=i; b=j; } Test max() //方法 max()的返回值类型是 Test 类型 { Test myTest = new Test(); myTest.mov(a+10,b+10);使实例变量的值增 10。 return myTest; } } class OutMyDemo extends java.applet.Applet; { public void paint(Graphics g) { Test p1 = new Test(); Test p2; p1.mov(15,55); g.drawString("p1 的实例变量值:a="+pa1.a+",b="+pa1.b,25,75); p2=p2.amx(); g.drawString("p2(p2 加 10)的值:a="+p2.a+",b="+p2.b,25,100); } } 程序运行的结果如下: p1 的实例变量值:a=15,b=55; p2(p1 加 10)的值:a=15,b=65; p2(p2 加 10)的值:a=35,b=75; 1.10 构造方法 1.10.1构造方法概述 构造方法是一个在创建对象初始化的方法,它具有与类相同的名字。事实上,除了构造 方法,在类中不能再有别的方法与类同名。一旦定义了构造方法,在执行 new 操作期间, 首先创建对象,然后自动调用构造方法将对象初始化。 与其他方法不同,构造方法必须在 new 运算符中引用,而不能按引用一般方法的格式 去引用构造方法。 在同一个类中,允许定义多个构造方法。这些构造方法是以参数的个数来区分的。在创 建对象时,程序员应当根据参数的个数和类型来选择合适的构造方法。 如果一个类中没有包含构造的说明,将提供隐含的构造方法,隐含的构造方法没有参数, 它将调用超类中不带参数的构造方法,如果超类中没有不带参数的构造方法,将产生编译错 误。 构造方法有些特殊,它没有返回类型甚至 void。因为构造方法的缺省返回类型是类类型 本身。构造方法的另一个特殊性是它不能继承超类。 例子: import java.awt.Graphics; class Test { double width; double height; double depth; Test(double w,double h,double d) //定义构造方法 Box() { width = w; height = h; depth=d; } double volume() { return width*height*depth; } } class Testing extends java.applet.applet { public void paint(Graphics g) { double d_volume; Test test = new Test()//new 运算符创建对象 test 后,随即对 test 初始化。 } } d_volume = test.volume();//计算体积 g.drawString("test 的体积是:"+d_volume,25,50); 程序运行的结果如下: test 的体积是:6000 1.10.2构造方法的访问权限 在定义构造方法时可以对他们附加访问修饰符,它们时: pulic(公有型的构造方法) private (私有型的构造访问) proected (保护型的构造方法) private protected (私有保护型的构造方法) 这四种方法访问修饰符的权限范围和方法的访问权限的范围一样。构咱方法的访问修饰符不 能是 abstract、static、final、native 或 synchronized; 1.11 方法重载 在 java 中,可以在同一个类中定义多个同名的方法,只要它们的参数列表不同,这叫做方 法的重载(Overload)。方法重载是 Java 最具有吸引力的有利特征。 当一个重载方法被激起时,Java 便根据参数的类型和数目确定被调用的方法。这样,重载方 法之间一定具有不同的参数列表,包括参数的类型和数目。 例:给定两个或三个数,将它们由大到小按顺序输出。 import java.awt.Graphics; class Test { double max1,max2,nax3; Test() { max1=-1; maxe2=-1; max3=-1; } void sort(double i,double j) { double s; max1=i; max2=j; if( max1Graphics g) { Testt1=new Testt(); t1.sort(200,300); g.drawString("两个数的大小顺序为:"+ t1.max1+","+t1.max2,25,10); t1.sort(100.9f,200.9f,300.9f); g.drawString("三个数的大小顺序为:"+ t1.max1+","+t1.max2+","+t1.max3,25,30); } } 程序运行的结果如下: 两个数的大小顺序为:300,200 三个数的大小顺序为:300.9,200.9,100.9 在上面的代码中,类 Test 中定义了三个方法: Test()是一个无参的构造方法,它在创建对象之后紧接着对变量进行初始化。如果没有该 方法,在创建对象时将产生编译错误。因为类 Test 是直接超类。 两个 sort()是排序方法,它们是用参数的数目来区分的,这两个 sort()方法能对任 何数字类型的数据进行排序。当然,对多个数据进行排序最好用数组来实现。 1.12 this 1.12.1用 this 引用成员变量 例子:在构造方法中使用 this,this 代表当前对象的引用参考,并将参数值赋值到成员 变量 max1、max2 和 max3 中。 import java.awt.Graphics; class Test { double max1; double max2; double max3; Test(double i,double j) { this.max1=i; this.max2=j; this.max3=k; } } class Test1 extends java.awt.Applet { public void paint(Graphics g) { Test t = new Test(10,20,30); g.drawString(t.max1+","+t.max2+","+t.max3,25,30); } } 程序运行的结果如下: 10,20,30 1.12.2在构造方法中用 this 调用一般方法 例子:在构造方法中使用 this 调用 sort()方法。 import java.awt.Graphics; class Test { double max1,max2,max3; Test(double i,double j) { max1=i; max2=j; this.sort(i,j); //调用 sort 方法,将两个数排大小顺序。 } Test(double i,double j,double k) { max1=i; max2=j; max3=k; this.sort(i,j,k) //调用 sort 方法,将三个数排大小顺序。 } void sort(int i,double j) { double s; if(max1Graphics g) { Test t1 = newTest(10,100); g.drawString("两个数的大小顺序为:"+t1.max1+","t1.max2,25,10); Test t2 = new Test(100,10,1000); g.drawString("三个数的大小顺序为:"+t2.max1+","t2.max2+","+t2.max3,25,30); } } 程序运行的结果如下: 两个数的大小顺序为:100,10 三个数的大小顺序为:1000,100,10 1.12.3在方法中用 this 调用另外一个方法 例子:在上面的例子上加一个方法 mov(),在 mov()中引用 this 再调用 sort()方法。 import java.awt.Graphics; class Test { double max1,max2,max3; Test(double i,double j) { max1=i; max2=j; this.sort(i,j); //调用 sort 方法,将两个数排大小顺序。 } void sort(int i,double j) { double s; if(max1Graphics g) { Test t1 = newTest(10,100); t1.mov(20,200); g.drawString("两个数的大小顺序为:"+t1.max1+","t1.max2,25,10); } } 程序运行的结果如下: 两个数的大小顺序为:200,20 注意:再初始化之后,变量 max1 和 max2 的值分别为 10 和 100,当调用 mov()之后, 其值为 20 和 200。 1.13 类的继承 继承是面向对象程序设计的重要概念之一。在 java 中,被继承类称为超类,继承称为 子类。通过继承可使子类继承超类的特征和功能。其基本规则如下: (1) 子类可继承其超类的代码和数据,即在子类中可以直接执行超类的方法和访问超类 的数据。 (2) 在子类中可以增加超类中没有的变量和方法。 (3) 在子类中可以重新定义超类中已有的变量(包括实例变量和类变量)。变量的重新 定义被称为变量的隐藏,也就是说,在子类中允许定义与超类同名的变量。 (4) 在子类中可以重载超类中已有的方法(包括构造方法、实例方法和类方法)。 (5) 在子类的构造方法中,可以通过 super()方法将超类的变量初始化。 (6) 当超类为抽象类时,子类可继承超类中的抽象方法,并在子类中去实现该方法的细 节。 1.13.1 创建子类 实现继承要用 extends 关键词,其格式如下: 子类名 extends 超类名 (1) 继承超类的方法和变量 例子:给定三个数,将它们按大到小的顺序输出。在下面的程序中,子类 Test1 继承超类 Test 的全部代码和变量。在子类的方法中,调用超类的方法 sort()和更 新数据。 import java.awt.Graphics; class Test { double max1,max2,max3; Test() { max1=-1 max2=-1; max3=-1; } void sort() { double s; if(max1Graphics g) { Test1t1 =new Test1(); t1.subsort(100,.300,200); g.drawString("三个数的大小顺序为:"+t1.max1+","t1.max2+","+t1.max3,25,30); } } 程序运行的结果如下: 三个数的大小顺序为:300,200,100 (2) 隐藏实例变量和类变量 在子类中,若定义了与超类同名的变量,则只有子类中的变量有效,而超类中 的变量无效。 例:数据求和。 import java.awt.Graphics; class Test { double sum,num1,num2; static int num3; Test() { num1=0; num2=0; num3=0; sum=0; } } class Test1 extendsTest { int sum,num1,num2; //隐藏超类 Test 中的实例变量 static int num3; //隐藏超类 Test 中的类变量 } void sum(int i,int j,int k) { num1=i; num2=j; num3=k; sum=num1+num2+num3; } class SumTest extends java.awt.Applet { public void paint(Graphics g) { Test1t1 =new Test1(); t1.sum(100,200,300); g.drawString("sum="+ t1.num1+"+"+t1.num2+"+"+t1.num+"="+t1.sum,25,30); } } 程序运行的结果如下: sum=100+200+300+=600 (3)访问权限 尽管子类可以继承超类的所有成员,但子类不能继承超类中的私有变量和方法,而只能 继承如下变量和方法。 .超类中定义为公有的、私有保护的或保护的成员变量和方法可被子类访问。 .同一个包中的超类的友好变量和方法可被子类访问。 (4)用继承的方法来扩充超类的功能 如果子类只继承超类的代码和数据是没有多大意义的,对继承来说,其真正的目的是想 通过子类来扩充超来的功能,只有这样,才能有效发挥继承的作用。 例:从超类 Box 中只计算"盒子"的体积,但在子类 SubBox 中不仅只计算"盒子"的 体积,而且还能求得它重量。 import java.awt.Graphics; class Box { double width; double height; double depth; Box(double w,double h,double d) { width=w; height=h; } Box() { depth=d; } width=-1; height=-1; depth=-1; } double volume() { return width*height*depth; } class SubBox extends Box { double weight; SubBox(double w,double h,double d,double m) { width=w; height=h; depth=d; weight=m; } } class Test extends java.awt.Applet { public void paint(Graphics g) { double d_volume; Box box1=new Box(10,20,30); d_volume=box1.volume(); //计算 box1 的体积 } } g.drawString("box1 的体积是:"+d_volume,25,50); SubBox subBox = new SubBox(15,15,20,40); d_volume= subBox.volume(); g.drawString("subBox 的体积是:"+d_volume,25,75); g.drawString("subBox 的体积是:"+subBox.weight,25,90); 程序运行的结果如下: box1 的体积是:6000; subBox 的体积是:4500 subBox 的重量是:40 1.13.2 重载超类的方法 要使多态性在程序中设计运用成功,不仅要求超类给子类提供可直接使用的元素,更重要的 是在类层次结构中具有一致的接口,使子类在超类的基础上扩充超类的功能。 例:超类 Test 中方法 sort()实现对 n 个整个升序排列,子类 Test1 中的 sort()实现对 n 个整数 按降序排列。 import java.awt.Graphics; class Test { int Test() { i=j=k=swap=0; } void sort(int t1,int t2[ ]) //用选择法按升序排列 { for(int i=0;iGraphics g) { int a[]={34,12,8,67,88,23,98,101,119,56}; g.drawString("排序前的数据为:",20,10); for(int i=0;i<10;i++) { g.drawString(""+a[i],20+i*30,30); } Test t1=new Test(); t1.sort(a.lenght,a); //调用 Test 类的方法 g.drawString("按升序排列的数据为:",20,50); for(int i=0;i<10;i++) { } g.drawString(""+a[i],20+i*30,70); } } Test1 t2=new Test2(); t2.sort(a.lenght,a); g.drawString("按降序排列的数据为:",20,90); for(int i=0;i<10;i++) { g.drawString(""+a[i],20+i*30,110); } 程序运行的结果如下: 排序前的数据为: 34 12 8 67 88 23 98 101 119 56 按升序排列的数据为: 8 12 23 34 56 67 88 98 101 119 按降序排列的数据为: 119 101 98 67 56 34 23 12 8 1.13.3 super super 是一个方法,其格式为: super(parameter-list) 其中,parameter-list 是参数列表。super()的作用是调用超类的构造方法。降超类中的变量初 始化。super()总是用于子类的构造方法中,而且在子类的构造方法中最先执行 super().因此, 只有 parameter-list 与超类的构造方法中的参数相匹配,才能有效调用超类的构造方法去实现 对超类的变量初始化。同时,在子类中也减少了初始化编码的重复工作。 super()的用法请见下面的例子: 例:在下面的例子里,将子类的构造方法中的赋初值改用 super()方法来完成 import java.awt.Graphics; class Box { double width; double height; double depth; Box(double w,double h,double d) { width=w; height=h; depth=d; } Box() { } width=-1; height=-1; depth=-1; } double volume() { return width* height* depth; } class SubBox extends Box { double weight; SubBox(double w,double h,double d,double m) { super(w,h,d); weight=m; } } class BoxTest extends java.awt.Applet { public void paint(Graphics g) { double d_volume; Box box1=new Box(10,20,30); d_volume=box1.volume(); //计算 box1 的体积 } } g.drawString("box1 的体积是:"+d_volume,25,50); SubBox subBox = new SubBox(15,15,20,40); d_volume= subBox.volume(); //计算 subBox 的体积 g.drawString("subBox 的体积是:"+d_volume,25,75); g.drawString("subBox 的体积是:"+subBox.weight,25,90); 程序运行的结果如下: box1 的体积是:6000; subBox 的体积是:4500 subBox 的重量是:40 但要注意的是: (1) super(w,h,d)用于子类的构造方法 SubBox()中。 (2) super(w,h,d)中的参数应与超类的构造方法参数相匹配,即参数个数及类型 一样。 1.13.4 abstract 当要求将一个超类定义为抽象类时,前面加关键字 abstact。其格式如下: abstract 类型 类名{} abstract 的作用是说明该类是一种抽象结构。抽象结构的类包含了一些抽象的方法,而这些 抽象方法只有方法的形式,即方法体是空的,方法体的细节由子类去实现。抽象方法的定义 也由关键字 abstract 来说明,其格式为: abstract 类型 方法名{parameter-list}; { 其中,parameter-list 是参数列表。因此,整个抽象类的结构如下形式: 成员变量 1; 。。。。。 构造方法 。。。。。。 abstract 类型 方法名(parameter-list); } 抽象类的定义也是多态的一种体现。因为多态性具有子类重载超类中的方法的特性,而在超 类中只限定子类重载规定的方法,但这些方法的细节必须由子类来完成。所有,常把这样的 类作为抽象类。 抽象类不能直接用 new 运算符实例化一个对象。抽象方法只能是实例化方法,它不包括子 类对象。 例:在抽象类 Test 中,定义一个抽象方法 sort(),并在子类 Test1,Test2 中,分别去实现超类中 的抽象方法 sort()的细节,从而分别完成对 n 个整数的降序、升序的排列。 import java.awt.Graphics; abstract class Test { int i,j,k,swap; Test() { i=j=k=swap=0; } abstract void sort(int t1,int[] t2); //定义一个抽象的方法。 } class Test1 extends Test { void sort(int t1,int t2[ ]) //重载超类的方法 sort(),用选择法按降序排列 { for(int i=0;iGraphics g) { int a[]={34,12,8,67,88,23,98,101,119,56}; //Test k=new Test(); 此句是非法的,不能创建实例对象。 Test1 k1=new Test1(); k1.sort(a.length,a); g.drawString("按降序排列的数据为",20,10); for(int i=0;i<10;i++) { g.drawString(""+a[i],20+i*30,30); } Test2 t2=new Test2(); t2.sort(a.lenght,a); g.drawString("按升序排列的数据为:",20,50); for(int i=0;i<10;i++) { g.drawString(""+a[i],20+i*30,70); } 程序运行的结果如下: 按降序排列的数据为: 119 101 98 67 56 34 23 12 8 按升序排列的数据为: 8 12 23 34 56 67 88 98 101 119 通过本例可以发现抽象类的好处:从一个抽象类可以派生出多个子类,而且在各个子类中允 许对同一个抽象方法产生不同的方法体。 1.13.5 final final 有三个用途:其一,可以用于创建与常量一样的变量,其二,另外 2 个用途就是防止 类被继承或方法被重载。 1. 最终类 Java 引入了最终类的概念,所谓最终类既是那些不能再有子类的类。说明最终类时可以 在最前面加上修饰符 final。 例:final class A { //.... } class B extends A //错误!不能从 A 类派生 { //....... } 注意:不能将一个抽象类说明为 final 类,因为抽象类必须要被派生类来实现它的抽象 方法。当一个类被说明为 final 类后,也相当于隐式的说明了它的所有方法为 final。 1.14 java 包,接口和异常 包是类的容器,用于保证类名空间的一致性。包以层次结构组织并可被明确地引入到一 个新类定义。 接口是方法的显示说明,利用接口可以完成没有具体实现的类。接口虽然与抽象类相似, 但它具有多继承能力。一个类可以有无数个接口,但是只能从一个父类派生。 异常是代码运行时出现地非正常状态。Java 的异常是一个出现在代码中描述异常状态的 对象。每当一个异常情况出现,系统就创建一个异常对象,并转入到引进异常的方法中,方 法就根据不同的类型捕捉异常。为防止由于异常而引起的退出,在方法退出前应执行特定的 代码段。 1.14.1 包 1.14.1.1 package 语句 包是一种命名和可视控制的机制,用户可以把某些类定义在一个包 中,也可以对定义在这个包中的类施加访问权限,以限定包外或包 内的程序对其中的某些类进行访问。 定义包的格式: package pkg; Java 的文件系统将存储和管理这个包。例如:属于 pkg 包中的.class 文件将存储到目录 pkg。注意:目录与包的名字完全一样。 package 语句可以创建包的层次结构,通过使用逗号,把每个包分开。 多级包层次结构的一般格式为: package pkg1[.pkg2[.pkg3]] 包层次结构必须反映 Java 开发系统的文件系统。比如:pacakge java.awt.Image; 1.14.1.2 import 语句 import 语句的功能是引入包或包中的类。它们必须位于 package 语 句与这个文件的类或接口之间。 格式:import pkg1[.pkg2.(classname|*)] 这里,pkg1 是顶层包名,pkg2 是下一层包名,其间用点隔开。除了 对文件系统的限制外,对包的层次深度没有限制。最后以一个显示 方式指定一个类名 classname 或*号,星号*指示 Java 编译器应该引入 整个包。 例子: import java.awt.Image; import java.lang.*; 1.14.2 接口 1.14.2.1 接口的概念 接口与类存在着本质的差别,类有它的成员变量和方法,而接口只有 常量和方法协议,从概念来讲,接口是一组方法协议和常量的集合。 接口在方法协议与方法体实体之间只起到一种称之为界面的作用,这 种界面限定了方法实体中的参数类型一定要与方法协议中所规定的 参数类型保持一致,除此以外,这种界面还限定了方法名、参数个数 及方法返回类型的一致性。因此,在使用接口时,类与接口之间并不 存在子类与父类的那种继承关系,在实现接口所规定的某些操作时只 存在类中的方法与接口之间保持一致的关系,而且一个类可以和多个 接口之间保持这种关系,即一个类可以实现多个接口。 1.14.2.2 接口的定义 格式: access interface name { return method-name(parameter-list); type final-varname=value; } 其中,access 是访问修饰符。它只有 public 和未指定访问修饰符 两种情况。当未指定访问修饰符时,该接口只能为同一个包中的 其他成员所用;当 public 修饰符说明时,该接口可以被其他任何 代码使用。name 是接口的名字,可以是任何有效的标识符。接 口体包括方法协议和常量,它们都用分号结束。在接口中定义的 常量必须用变量名标识。他们隐式为 final 和 static,不能被实现 类所改变。 例:interface Back { void c_back(int param); int SOON=4; } 该接口定义了一个方法协议,其参数为整数类型;还定义了一整 数常量,其变量名为 SOON。该接口只能为同一个包中其他成员 所用。 1.14.2.3 接口的实现 一旦接口被定义,类就可以实现它。在类的定义中可以使用关键 字 implements 实现接口,然后在类中创建接口中所定义的方法, implements 关键字的类的格式: access class classname[extends superclass] [implements interfacename[,interface...]] { } ..... 例子:定义两个接口,其方法协议分别完成两个数的加法和减法 操作,然后分别实现这两个接口的方法。 import java.awt.Graphics; interface Demo_Add { public int add(int x,int y); } interface Demo_Sub { public int sub(int x,int y); } class Demo_Add_Sub implements Demo_Add, Demo_Sub { public int add(int x,int y) { return i+y; } public int sub(int x,int y) { return x-y; } } public class Add_Sub extends java.awt.Applet { public void paint(Graphics g) { Demo_Add_Sub k=new Demo_Add_Sub(); g.drawString("x+y="+k.add(30,10),30,30); g.drawString("x-y=" + k.sub(30,10),30,50); } } 程序运行结果如下: x+y=40; x-y=20; 1.14.2.4 接口的继承 一个接口可以继承其他接口,这可通过关键字 extends 来实现,其语 法与类的继承相同。当一个类实现一个派生接口时,必须实现所有 接口及其派生接口中所定义的全部方法协议。 例:定义 5 个接口,其中两个超接口。 import java.awt.Graphics; //定义接口 Test_C interface Test_C { public int add(int x,int y); } interface Test_B extends Test_C { public int sub(int x,int y); } interface Test_A extends Test_B { public int mul(int x,int y); } //定义接口 Test_Y interface Test_Y { public int div (int x,int y); } interface Test_X extends Test_Y { public int mod (int x,int y); } //定义类 Test class Test implements Test_A ,Test_X { public int add(int x,int y) { return x+y; } public int sub(int x,int y) { } return x-y; } public int mul(int x,int y) { return x*y; } public int div (int x,int y) { return x/y; } public int mod (int x,int y) { return x%y; } public class Add_Mod extends java.awt.Applet { public void paint(Graphics g) { Test k =new Test(); g.drawString("x+y="+k.add(66,10),30,10); g.drawString("x-y="+k.sub(66,10),30,10); g.drawString("x*y="+k.mul(66,10),30,10); g.drawString("x/y="+k.div(66,10),30,10); g.drawString("x%y="+k.mod(66,10),30,10); } } 程序运行结果如下; x+y=76 x-y=56 x*y=660 x/y=6 x%y=6 注意:在类中实现一个接口时,任何类都必须实现该接口或接口树的所有方法。 1.14.3 异常处理 所谓异常就是代码在运行时发生的非正常状态。Java 的异常是一个出现在代码中描 述异常状态的对象。每当出现一个异常情况,就创建一个异常对象,并向导致错误 的方法中抛出该对象。该方法捕捉到异常对象之后,可以选择用来处理该异常或不 管它。异常通常由 Java 运行系统产生,或者由用户的程序代码产生。Java 抛出的异 常大多是用户违背了 Java 语言的基本规则,或者超出了 Java 执行环境的约束。 1.14.4 异常处理机制 Java 通过 5 个关键字 try、catch、throw、throws 和 finally 管理异常处理。 try 用来监视它所在的那个程序块是否发生异常,如果发生异常就抛出它。对于系统 产生的异常或程序块中未用 try 监视所产生的异常,将一律被 Java 运行系统自动抛 出。 catch 用来捕捉 try 程序所抛出的异常,将一律被 Java 运行系统自动抛出。 throw 可以用以人工地抛出一个异常 throws 用于从一个方法中抛出一个异常。 finally 用于调用缺省异常处理。 下面是异常处理地一般形式: try { //有可能会出错地代码块,被 try 监视。 } catch(ExceptionType1 ex) { //关于 ExceptionType1 的异常处理 } catch(ExceptionType2 ex) { //关于 ExceptionType2 的异常处理 } //...... finally { //在 try 块结束前被执行的代码块。 } 1.2 常用类 System 类 全称:java.lang.System 扩展:Object 描述:公有最终类。此类与 Runtime 一起可以访问许多有用的系统功能。有一些方法在 两个类中都出现。exit()、gc()、load()和 loadLibrary()可以通过调用 Runtime 中的同名方 法来响应。特别有用的是类变量 err、in 和 out,它们可以访问基本控制台 I/O。System 类完全由类域组成,这些类域可以通过"System.变量名"和"System.方法()"的方法 进行访问。此类不能被实例化。 实例变量: public static PrintStream err; public static InputStream in; public static PrintStream out; 每个变量对整个程序而言都是唯一的对象变量。这些对象可以访问系统的输入、输出和 错误输出。 常用类方法: void exit(int status) 导致 Java 程序退出,向系统传递指定的状态代码。 void gc() 请求立即激活垃圾收集器开始清除不再使用的对象。 String getProperty(String key) String getProperty(String key,String def) 在系统属性表中查找的一个属性。如果未发现属性,而且在 getProperty()方法中指定了 参数 def,那么返回 def。 SwingUtil.ities 类 全称:javax.swing.SwingUtilities 扩展:Object 描述:公用类。此类中包含了一组在 Swing 工具包中使用的帮助器方法 常用类方法: void paintComponent(Graphics g,Component c,Container p,int x,int y,int w,int h); void paintComponent(Graphics g,Component c,Container p,Rectangle r); void invokeLate(Runnable doRun); boolean isEventDispatchThread(); Component getRoot(Component c); JRootPane getRootPane(Component c);

62,025

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术交流专区
javascript云原生 企业社区
社区管理员
  • ASP.NET
  • .Net开发者社区
  • R小R
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

.NET 社区是一个围绕开源 .NET 的开放、热情、创新、包容的技术社区。社区致力于为广大 .NET 爱好者提供一个良好的知识共享、协同互助的 .NET 技术交流环境。我们尊重不同意见,支持健康理性的辩论和互动,反对歧视和攻击。

希望和大家一起共同营造一个活跃、友好的社区氛围。

试试用AI创作助手写篇文章吧