数据专栏

智能大数据搬运工,你想要的我们都有

科技资讯:

科技学院:

科技百科:

科技书籍:

网站大全:

软件大全:

相关推荐: JPopupButton Swing开发之JEditorPane篇 有关GridBagLayout的用法 virtualbox:摄像头无显示:V4L2_CORE: Could not grab image (select timeout): Resource temporarily unavailable JavaCV入门指南:帧抓取器(FrameGrabber)的原理与应用 javaCV入门指南:调用FFmpeg原生API和JavaCV是如何封装了FFmpeg的音视频操作? SWT学习笔记(2)——转载倪大鹏的“SWT:AWT和SWING的强大竞争者” class VideoCapture之VideoCapture::grab
推荐群组: JSF
更多相关推荐
OO
Java 的乐趣与游戏:Java grab 包的技术提示
一些 Java SE 技术提示
作者:Jeff Friesen, JavaWorld.com, 01/02/07 翻译:suli原文地址:http://www.javaworld.com/javaworld/jw-01-2007/jw-0102-games.html 开发 Java 平台十年之久,我已经积累了一些使用 Java SE 的 grab 包加强游戏及其他 Java 平台开发的宝贵经验。 本期的 Java Fun and Games 将与您分享一些技术提示。 在文章的后半部分,将介绍如何将这些技术提示应用到一个网页抓图应用程序。
最简单的 API
不管计算机运行得有多快,我们却总是在等待某个任务的完成,比如,下载大个的文件、执行彻底搜索或者进行复杂的数学计算。 在这些费时的任务完成时,许多 Java 程序都会用一些花哨的方式提示用户,普遍方法是使用可以听得见的警告。
Java 提供了许多声音 API 可以用于创建有声警告。 可以使用 Java Speech API 告诉用户任务已经结束。 如果您希望任务完成时播放音效或音乐,Java Sound API 是一个不错的选择。 然而,因为 Java Speech 需要额外的分发文件,而 Java Sound 需要相当复杂的代码,您可能就希望使用 Audio Clip API 了。
Audio Clip API 基于 java.applet.AudioClip 和 java.applet.Applet 方法,例如: public static final AudioClip newAudioClip(URL url) 。 虽然此 API 比 Java Speech 和 Java Sound 更易于使用,但只用它来播放一段简单的声音也太过大材小用了。 对于这种简单的任务,还是考虑使用 Java 最简单的声音 API 吧。
最简单的声音 API 由 java.awt.Toolkit 的 public abstract void beep() 方法构成。 当调用此方法时,将发出简单的“哔跸”声。 为了展示 beep() 的用法,我创建了一个 CalcPi 应用程序,为 Pi 计数。 请看列表 1。
列表 1 CalcPi.java
// CalcPi.java
import java.awt.Toolkit;
import java.math.BigDecimal;
public class CalcPi
{
/* constants used in pi computation */
private static final BigDecimal ZERO = BigDecimal.valueOf (0);
private static final BigDecimal ONE = BigDecimal.valueOf (1);
private static final BigDecimal FOUR = BigDecimal.valueOf (4);
/* rounding mode to use during pi computation */
private static final int roundingMode = BigDecimal.ROUND_HALF_EVEN;
/* digits of precision after the decimal point */
private static int digits;
public static void main (String [] args)
{
if (args.length != 1)
{
System.err.println ("usage: java CalcPi digits");
return;
}
int digits = 0;
try
{
digits = Integer.parseInt (args [0]);
}
catch (NumberFormatException e)
{
System.err.println (args [0] + " is not a valid integer");
return;
}
System.out.println (computePi (digits));
Toolkit.getDefaultToolkit ().beep ();
}
/*
* Compute the value of pi to the specified number of
* digits after the decimal point. The value is
* computed using Machin's formula:
*
* pi/4 = 4*arctan(1/5) - arctan(1/239)
*
* and a power series expansion of arctan(x) to
* sufficient precision.
*/
public static BigDecimal computePi (int digits)
{
int scale = digits + 5;
BigDecimal arctan1_5 = arctan (5, scale);
BigDecimal arctan1_239 = arctan (239, scale);
BigDecimal pi = arctan1_5.multiply (FOUR).
subtract (arctan1_239).multiply (FOUR);
return pi.setScale (digits, BigDecimal.ROUND_HALF_UP);
}
/*
* Compute the value, in radians, of the arctangent of
* the inverse of the supplied integer to the specified
* number of digits after the decimal point. The value
* is computed using the power series expansion for the
* arc tangent:
*
* arctan(x) = x - (x^3)/3 + (x^5)/5 - (x^7)/7 +
* (x^9)/9 ...
*/
public static BigDecimal arctan (int inverseX, int scale)
{
BigDecimal result, numer, term;
BigDecimal invX = BigDecimal.valueOf (inverseX);
BigDecimal invX2 = BigDecimal.valueOf (inverseX * inverseX);
numer = ONE.divide (invX, scale, roundingMode);
result = numer;
int i = 1;
do
{
numer = numer.divide (invX2, scale, roundingMode);
int denom = 2 * i + 1;
term = numer.divide (BigDecimal.valueOf (denom), scale, roundingMode);
if ((i % 2) != 0)
result = result.subtract (term); else
result = result.add (term);
i++;
}
while (term.compareTo (ZERO) != 0);
return result;
}
}
列表 1 使用一种算法来计算 pi,该算法是早在 18 世纪初期由英国数学家 John Machin 发明的。 算法首先计算 pi/4 = 4*arctan(1/5)-arctan(1/239),然后将结果乘以 4 得出 pi 的值。 由于 arc (inverse) tangent 是使用一系列庞大的 term 来计算的, term 的数量越大得出的 pi 值越准确(小数点后显示的位数)
注意 列表 1 的大部分代码引用自 Sun 的 远程方法调用 教程的“创建一个客户端程序”部分。
此算法的实现依赖于 java.math.BigDecimal 和一个 arc-tangent 方法。 虽然 Java SE 5.0 等高级版本的 BigDecimal 包括常量 ZERO 和 ONE ,这些常量在 Java 1.4 中是不存在的。 同样,number-of-digits 命令行参数用于确定 arc-tangent 的数量和 pi 的精确度。
java CalcPi 0
3
java CalcPi 1
3.1
java CalcPi 2
3.14
java CalcPi 3
3.142
java CalcPi 4
3.1416
java CalcPi 5
3.14159
本文中更重要的部分是 Toolkit.getDefaultToolkit ().beep (); ,该语句用于在计算结束时发出“哗哗”的声音。 由于数字参数越大造成的计算时间越长,此声音可以让您知道 pi 的计算何时结束。 如果一声“哗”响不够用,可以按照如下方法创建其他的音效:
Toolkit tk = Toolkit.getDefaultToolkit ();
for (int i = 0; i < NUMBER_OF_BEEPS; i++)
{
tk.beep ();
// On Windows platforms, beep() typically
// plays a WAVE file. If beep() is called
// before the WAVE sound finishes, the
// second WAVE sound will not be heard. A
// suitable delay solves this problem.
// (I'm not sure if this problem occurs
// on other platforms.)
try
{
Thread.sleep (BEEP_DELAY);
}
catch (InterruptedException e)
{
}
}
窗口居中
使您的 Java 程序看起来更加专业的一种方法就是使其对话框窗口(例如,一个 "about" 窗口)位于父窗口的中间。 可以使用 java.awt.Window 的 public void setLocationRelativeTo(Component c) 方法完成此任务,该方法将相对于组件参数 — null 来创建一个居于屏幕中间的窗口。 请看列表 2。
列表 2 AboutBox1.java
// AboutBox1.java
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.border.*;
public class AboutBox1
{
public static void main (String [] args)
{
final JFrame frame = new JFrame ("AboutBox1");
frame.setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE);
JPanel panel = new JPanel ()
{
{
JButton btnWindow = new JButton ("Window center");
ActionListener l = new ActionListener ()
{
public void actionPerformed (ActionEvent e)
{
new AboutBox (frame, "W").setVisible (true);
}
};
btnWindow.addActionListener (l);
add (btnWindow);
JButton btnScreen = new JButton ("Screen center");
l = new ActionListener ()
{
public void actionPerformed (ActionEvent e)
{
new AboutBox (frame, "S").setVisible (true);
}
};
btnScreen.addActionListener (l);
add (btnScreen);
}
};
frame.getContentPane ().add (panel);
// frame.setLocationRelativeTo (null);
frame.pack ();
// frame.setLocationRelativeTo (null);
frame.setVisible (true);
}
}
class AboutBox extends JDialog
{
AboutBox (JFrame frame, String centerMode)
{
super (frame, "AboutBox", true /* modal */);
final JButton btnOk = new JButton ("Ok");
btnOk.addActionListener (new ActionListener ()
{
public void actionPerformed (ActionEvent e)
{
dispose ();
}
});
getContentPane ().add (new JPanel () {{ add (btnOk); }});
pack ();
setLocationRelativeTo (centerMode.equals ("W") ? frame : null);
}
}
Listing 2's AboutBox1 创建了一个 GUI,它的两个按钮建立了一个 "about" 对话框,该对话框通过 setLocationRelativeTo() 方法位于应用程序主窗口或屏幕的中心位置。 frame.pack (); 之前被注释掉的一行无法起到使主窗口居中的作用,因为主窗口的大小还没有确定。 但是,被注释掉的第二行起到了使窗口居中的作用。
getContentPane ().add (new JPanel () {{ add (btnOk); }}); 也许看起来有点奇怪,因为它嵌套了许多括号。 本质上该语句可以理解为,创建一个内部匿名类(该类扩展自 javax.swing.JPanel )的对象,通过由内层括号对标识的 object block initializer 为此对象添加一个按钮,然后将对象添加到对话框的 content pane 中。
添加阴影
如 果您想突出显示一个 "about" 对话框的标题文字,可以考虑以一定的偏移量和指定的颜色绘制背景文字以达到投放“阴影”的效果。 选择一个适当的颜色做为背景字的颜色,注意与前景文字和背景的颜色搭配。 然后使用反失真技术使边缘上的小锯齿变得平滑。 效果如图 1 所示:

图 1:阴影可以强调对话框的标题
图 1 展示了一个具有蓝色文字、黑色阴影和白色背景的 "about" 对话框。 该对话框是在 AboutBox2 程序的 AboutBox(JFrame frame, String centerMode) 构造函数内创建的。 由于该程序的代码与 AboutBox1.java 极为相似,所以我只给出其构造函数:
AboutBox (JFrame frame, String centerMode)
{
super (frame, "AboutBox", true /* modal */);
// Add a panel that presents some text to the dialog box's content pane.
getContentPane ().add (new JPanel ()
{
final static int SHADOW_OFFSET = 3;
{
// Establish the drawing panel's preferred
// size.
setPreferredSize (new Dimension (250, 100));
// Create a solid color border that both
// surrounds and is part of the drawing
// panel. Select the panel background
// color that is appropriate to this look
// and feel.
Color c =
UIManager.getColor ("Panel.background");
setBorder (new MatteBorder (5, 5, 5, 5, c));
}
public void paintComponent (Graphics g)
{
// Prevent jagged text.
((Graphics2D) g).setRenderingHint
(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
// Because the border is part of the panel,
// we need to make sure that we don't draw
// over it.
Insets insets = getInsets ();
// Paint everything but the border white.
g.setColor (Color.white);
g.fillRect (insets.left, insets.top,
getWidth ()-insets.left-
insets.right,
getHeight ()-insets.top-
insets.bottom);
// Select an appropriate text font and
// obtain the dimensions of the text to be
// drawn (for centering purposes). The
// getStringBounds() method is used instead
// of stringWidth() because antialiasing is
// in effect -- and the documentation for
// stringWidth() recommends use of this
// method whenever the antialiasing or
// fractional metrics hints are in effect.
g.setFont (new Font ("Verdana",
Font.BOLD,
32));
FontMetrics fm = g.getFontMetrics ();
Rectangle2D r2d;
r2d = fm.getStringBounds ("About Box", g);
int width = (int)((Rectangle2D.Float) r2d)
.width;
int height = fm.getHeight ();
// Draw shadow text that is almost
// horizontally and vertically (the
// baseline) centered within the panel.
g.setColor (Color.black);
g.drawString ("About Box",
(getWidth ()-width)/2+
SHADOW_OFFSET,
insets.top+(getHeight()-
insets.bottom-insets.top)/2+
SHADOW_OFFSET);
// Draw blue text that is horizontally and
// vertically (the baseline) centered
// within the panel.
g.setColor (Color.blue);
g.drawString ("About Box",
(getWidth ()-width)/2,
insets.top+(getHeight()-
insets.bottom-insets.top)/2);
}
}, BorderLayout.NORTH);
final JButton btnOk = new JButton ("Ok");
btnOk.addActionListener (new ActionListener ()
{
public void actionPerformed (ActionEvent e)
{
dispose ();
}
});
getContentPane ().add (new JPanel () {{ add (btnOk); }},
BorderLayout.SOUTH);
pack ();
setLocationRelativeTo (centerMode.equals ("W") ? frame : null);
}
除了向您介绍如何在 JPanel 子类组件的 public void paintComponent(Graphics g) 方法中呈现阴影以外,构造函数还揭示了一个技巧:使用 UIManager.getColor("Panel.background") 获取与对话框背景色匹配的组件边框的颜色(即现在的外观)。
超级链接和启动浏览器
许多程序都会在 "about" 对话框中呈现超级链接。单击超级链接时,程序将启动默认浏览器,并为用户打开应用程序的网站。我想 "about" 对话框中的超级链接是可以用类来描述的,所以我创建了一个 AboutBox3 应用程序来说明其可行性。请阅读以下代码:
AboutBox (JFrame frame, String centerMode)
{
super (frame, "AboutBox", true /* modal */);
// Create a pane that presents this dialog box's text. Surround the pane
// with a 5-pixel empty border.
Pane pane = new Pane (5);
pane.setPreferredSize (new Dimension (250, 100));
// Create a title with a drop shadow for the pane.
Font font = new Font ("Verdana", Font.BOLD, 32);
Pane.TextNode tn = pane.new TextNode ("About Box", font, Color.blue,
Pane.TextNode.CENTERX,
Pane.TextNode.CENTERY, Color.black);
pane.add (tn);
// Create a link for the pane.
font = new Font ("Verdana", Font.BOLD, 12);
tn = pane.new TextNode ("Jeff Friesen", font, Color.blue,
Pane.TextNode.CENTERX, 80,
null, "http://www.javajeff.mb.ca", Color.red);
pane.add (tn);
// Add pane to the center region of the dialog box's content pane.
getContentPane ().add (pane);
// Create a button for disposing the dialog box.
final JButton btnOk = new JButton ("Ok");
btnOk.addActionListener (new ActionListener ()
{
public void actionPerformed (ActionEvent e)
{
dispose ();
}
});
// Add button via an intermediate panel that causes button to be laid
// out at its preferred size to the south region of the dialog box's
// content pane.
getContentPane ().add (new JPanel () {{ add (btnOk); }},
BorderLayout.SOUTH);
// Resize all components to their preferred sizes.
pack ();
// Center the dialog box with respect to the frame window or the screen.

setLocationRelativeTo (centerMode.equals ("W") ? frame : null);
}
AboutBox(JFrame frame, String centerMode) 构造函数创建了一个 Pane 组件,来描述一个用于绘制文字的区域。该组件的 Pane(int borderSize) 构造函数使用 borderSize 参数,来识别组件边框的大小(以像素为单位)-- 绘制区域的大小等于 Pane 的大小减去边框大小:
Pane (int borderSize)
{
// Create a solid color border that both surrounds and is part of the
// this component. Select the panel background color that is appropriate
// to this look and feel.
setBorder (new MatteBorder (borderSize, borderSize, borderSize,
borderSize,
UIManager.getColor ("Panel.background")));
}
该组件将文字存储为 Pane.TextNode 对象的数组列表。每个 TextNode 描述一个文字条目,并且创建自三个构造函数之一。最简单的构造函数是 TextNode(String text, Font font, Color color, int x, int y) ,它用于创建一个非超级链接且没有阴影的文字节点。使用的五个参数是: text 指定要绘制的文字 font 指定要使用的字体 font 指定要使用的文字颜色 x 指定第一个字符的起始列 y 指定每个字符基线所在的行
第二简单的构造函数是: TextNode(String text, Font font, Color color, int x, int y, Color shadowColor) .除了上述参数外,还需要为该函数指定 shadowColor ,即阴影的颜色。如果传递 null ,则不呈现阴影,这样一来该构造函数就与前一构造函数一样了。这两个构造函数都调用下面的第三个构造函数:
TextNode (String text, Font font, Color color, int x, int y,
Color shadowColor, String url, Color activeLinkColor)
{
this.text = text;
this.font = font;
this.color = color;
this.x = x;
this.y = y;
this.shadowColor = shadowColor; this.url = url;
this.activeLinkColor = activeLinkColor;
if (url != null)
{
addMouseListener (new MouseAdapter ()
{
public void mousePressed (MouseEvent e)
{
int mx = e.getX ();
int my = e.getY ();
if (mx >= TextNode.this.x &&
mx < TextNode.this.x+width &&
my > TextNode.this.y-height &&
my <= TextNode.this.y)
{
active = false;
repaint ();
}
}
public void mouseReleased (MouseEvent e)
{
int mx = e.getX ();
int my = e.getY ();
if (mx >= TextNode.this.x &&
mx < TextNode.this.x+width &&
my > TextNode.this.y-height &&
my <= TextNode.this.y)
{
active = true;
repaint ();
Launcher.
launchBrowser (TextNode.this.url);
}
}
});
addMouseMotionListener (new MouseMotionListener ()
{
public void mouseMoved (MouseEvent e)
{
int mx = e.getX ();
int my = e.getY ();
if (mx >= TextNode.this.x &&
mx < TextNode.this.x+width &&
my > TextNode.this.y-height &&
my <= TextNode.this.y)
{
if (!active)
{
active = true;
repaint ();
}
}
else
{
if (active)
{
active = false;
repaint ();
}
}
}
public void mouseDragged (MouseEvent e)
{
}
});
}
}
保存参数后,该构造函数会在 Pane 中注册一个鼠标监听器(假设 url 不为 null )。这些侦听器将判断鼠标指针是否位于超级链接文本上。如果是,则操纵 active 变量,将呈现该节点及其他节点,并启动浏览器。
class Launcher
{
static void launchBrowser (String url)
{
try
{
// Identify the operating system.
String os = System.getProperty ("os.name");
// Launch browser with URL if Windows. Otherwise, just output the url
// to the standard output device.
if (os.startsWith ("Windows"))
Runtime.getRuntime ()
.exec ("rundll32 url.dll,FileProtocolHandler " + url);
else
System.out.println (url);
}
catch (IOException e)
{
System.err.println ("unable to launch browser");
}
}
}
Pane 的 public void paintComponent(Graphics g) 方法将在该组件及其文本节点呈现的时候被调用。该方法启用反失真技术(防止文字出现锯齿),获取组件的插入位置(所以文本不会落在边框上),清理绘制区域使其成为白色,并呈现数组列表存储的每个文字节点。
public void paintComponent (Graphics g)
{
// Prevent jagged text.
((Graphics2D) g).setRenderingHint (RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
// Because the border is part of the panel, we need to make sure that we
// don't draw over it.
Insets insets = getInsets ();
// Paint everything but the border white.
g.setColor (Color.white);
g.fillRect (insets.left, insets.top, getWidth ()-insets.left-insets.right,
getHeight ()-insets.top-insets.bottom);
// Render all nodes.
Iterator iter = nodes.iterator ();
while (iter.hasNext ())
{
TextNode tn = (TextNode) iter.next ();
tn.render (g, insets);
}
}
每个文本节点都是由 TextNode 的 void render(Graphics g, Insets insets) 方法呈现的。该方法首先确定了字体,然后调用私有的 strDim() 方法来获取要绘制文字的规格尺寸等。然后呈现文本(可以选择阴影、超级链接等属性):
void render (Graphics g, Insets insets)
{
g.setFont (font);
Dimension d = strDim (g, text);
width = (int) d.width;
height = (int) d.height;
// Always drop the drop shadow (if specified) first.
if (shadowColor != null)
{
g.setColor (shadowColor);
if (x == CENTERX)
x = (getWidth ()-d.width)/2;
if (y == CENTERY)
y = insets.top+(getHeight ()-insets.bottom-insets.top)/2;
// Draw the drop shadow.
g.drawString (text, x+SHADOW_OFFSET, y+SHADOW_OFFSET);
}
// If the text is not a link, active can never be true -- the mouse
// listeners are not installed.
g.setColor ((active) ? activeLinkColor: color);
// If a drop shadow was drawn, x and y will never equal CENTERX and
// CENTERY (respectively). This is okay because x and y must contain
// the same values as specified when drawing the drop shadow.
if (x == CENTERX)
x = (getWidth ()-d.width)/2;
if (y == CENTERY)
y = insets.top+(getHeight ()-insets.bottom-insets.top)/2;
// Draw the text.
g.drawString (text, x, y);
}
g.setColor ((active) ? activeLinkColor: color); 决定是否绘制有效的超级链接文本(使用由 activeLinkColor 指定的有效链接颜色),或者使用由 color 指定的颜色绘制无效超级链接文本(或非超级链接文本)。图 2 显示了此决定的结果:
图 2 当鼠标指针移动到文本上时,超级链接的文本变成了红色。单击可查看大图。
状态栏
许多应用程序都会呈现状态栏,以显示程序的名称和版本、相应于菜单的帮助文字、当前时间以及一些其他信息。由于状态栏如此有用,您可能认为 Java 包含了一个 javax.swing.JStatusBar 组件。然而,事实并非如此。幸运的是,创建自己的状态栏还算容易,如图 3 所示。

图 3. 状态栏中当前菜单项的帮助文字
图 3 显示了一个由 StatBar 应用程序创建的状态栏。该应用程序将一个 javax.swing.JLabel 组件和一个 javax.swing.event.MenuListener 结合在一起,然后使用 java.awt.event.MouseListener 显示相应于菜单的菜单项帮助文字 -- 或者在未选择菜单或菜单项时显示默认文字。请看列表 3。
列表 3. StatBar.java
// StatBar.java
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
public class StatBar extends JFrame
{
// The status label serves as this application's status bar. A description
// of the currently highlighted menu/item appears on the status bar.
JLabel status;
// The default text appears on the status bar at program startup, and when
// no other menu/item text appears.
String defaultStatusText = "Welcome to StatBar 1.0!";
// The MenuItem helper class conveniently organizes the menu items for each
// of the File and Edit menus. This organization reduces the amount of
// source code that appears in the StatBar() constructor, which hopefully
// makes it easier to study the constructor, and facilitates adding extra
// menu items in the future.
class MenuItem
{
String label; // menu text
ActionListener al; String desc; // menu description for status bar
MenuItem (String label, ActionListener al, String desc)
{
this.label = label;
this.al = al;
this.desc = desc;
}
}
// Construct StatBar's GUI and indirectly start AWT helper threads.
public StatBar (String title)
{
// Pass application title to superclass, so that it appears on the title
// bar.
super (title);
// When the user initiates a close operation from the System menu or by
// clicking the tiny x window on a Microsoft Windows' window title bar,
// terminate this application.
setDefaultCloseOperation (EXIT_ON_CLOSE);
// Construct the application's menu bar.
JMenuBar mb = new JMenuBar ();
// Create a menu listener shared by all menus on the menu bar. This menu
// listener either displays default text or menu-specific text on the
// status bar.
MenuListener menul;
menul = new MenuListener ()
{
public void menuCanceled (MenuEvent e)
{
}
public void menuDeselected (MenuEvent e)
{
status.setText (defaultStatusText);
}
public void menuSelected (MenuEvent e)
{
JMenu m = (JMenu) e.getSource ();
status.setText (m.getActionCommand ());
}
};
// Create a mouse listener shared by all menu items on all menus. This
// mouse listener displays menu-item specific text on the status bar
// whenever the mouse pointer enters the menu item. It displays default
// text when the mouse pointer exits a menu item.
MouseListener statusl = new MouseAdapter ()
{
public void mouseEntered (MouseEvent e)
{
JMenuItem mi = (JMenuItem) e.getSource ();
status.setText (mi.getActionCommand ());
}
public void mouseExited (MouseEvent e)
{
status.setText (defaultStatusText);
}
};
// The first menu to appear on the menu bar is File. The user invokes
// menu items on this menu to open, save, and print documents, and to
// terminate the application.
JMenu menuFile = new JMenu ("File");
menuFile.addMenuListener (menul);
menuFile.setActionCommand ("Open document, save changes, print document "
+ "and terminate StatBar.");
// Create a listener for each menu item on the File menu.
ActionListener openl;
openl = new ActionListener ()
{
public void actionPerformed (ActionEvent e)
{
System.out.println ("Open listener invoked.");
}
};
ActionListener saveasl;
saveasl = new ActionListener ()
{
public void actionPerformed (ActionEvent e)
{
System.out.println ("Save as listener invoked.");
}
};
ActionListener savel;
savel = new ActionListener ()
{
public void actionPerformed (ActionEvent e)
{
System.out.println ("Save listener invoked.");
}
};
ActionListener printl;
printl = new ActionListener ()
{
public void actionPerformed (ActionEvent e)
{
System.out.println ("Print listener invoked.");
}
};
ActionListener exitl;
exitl = new ActionListener ()
{
public void actionPerformed (ActionEvent e)
{
System.exit (0);
}
};
// Identify menu items to be installed on the File menu.
MenuItem [] itemsFile =
{
new MenuItem ("Open...", openl, "Open a document."),
new MenuItem ("Save", savel, "Save changes to current document."),
new MenuItem ("Save as...", saveasl, "Save current document to new "
+ "document."),
new MenuItem ("Print...", printl, "Print current document."),
new MenuItem (null, null, null),
new MenuItem ("Exit", exitl, "Terminate StatBar.")
};
// Install all of the previous menu items on the File menu.
for (int i = 0; i < itemsFile.length; i++)
{
if (itemsFile [i].label == null)
{
menuFile.addSeparator ();
continue;
}
JMenuItem mi = new JMenuItem (itemsFile [i].label);
mi.addActionListener (itemsFile [i].al);
mi.setActionCommand (itemsFile [i].desc);
mi.addMouseListener (statusl);
menuFile.add (mi);
}
// Add the file menu to the menu bar.
mb.add (menuFile);
// The second menu to appear on the menu bar is Edit. The user invokes
// menu items on this menu to undo any changes and perform copy/cut/paste
// operations on the current document.
JMenu menuEdit = new JMenu ("Edit");
menuEdit.addMenuListener (menul);
menuEdit.setActionCommand ("Perform various editing tasks and undo " +
"changes.");
// Create a listener for each menu item on the Edit menu.
ActionListener undol;
undol = new ActionListener ()
{
public void actionPerformed (ActionEvent e)
{
System.out.println ("Undo listener invoked.");
}
};
ActionListener copyl;
copyl = new ActionListener ()
{
public void actionPerformed (ActionEvent e)
{
System.out.println ("Copy listener invoked.");
}
};
ActionListener cutl;
cutl = new ActionListener ()
{
public void actionPerformed (ActionEvent e)
{
System.out.println ("Cut listener invoked.");
}
};
ActionListener pastel;
pastel = new ActionListener ()
{
public void actionPerformed (ActionEvent e)
{
System.out.println ("Paste listener invoked.");
}
};
// Identify menu items to be installed on the Edit menu.
MenuItem [] itemsEdit =
{
new MenuItem ("Undo", undol, "Restore document."),
new MenuItem (null, null, null),
new MenuItem ("Copy", copyl, "Copy text to clipboard."),
new MenuItem ("Cut", cutl, "Cut text to clipboard."),
new MenuItem ("Paste", pastel, "Paste text from clipboard.")
};
// Install all of the previous menu items on the Edit menu.
for (int i = 0; i < itemsEdit.length; i++)
{
if (itemsEdit [i].label == null)
{
menuEdit.addSeparator ();
continue;
}
JMenuItem mi = new JMenuItem (itemsEdit [i].label);
mi.addActionListener (itemsEdit [i].al);
mi.setActionCommand (itemsEdit [i].desc);
mi.addMouseListener (statusl);
menuEdit.add (mi);
}
// Add the edit menu to the menu bar.
mb.add (menuEdit);
// Install StatBar's menu bar.
setJMenuBar (mb);
// Create a status bar for displaying help text associated with the menus
// and their items.
status = new JLabel (defaultStatusText);
status.setBorder (BorderFactory.createEtchedBorder ());
// Add the status bar to the bottom of the application's contentpane.
getContentPane ().add (status, BorderLayout.SOUTH);
// Establish a suitable initial size for displaying a document.
setSize (450, 300);
// Display GUI and start GUI processing.
setVisible (true);
}
// Application entry point.
public static void main (String [] args)
{
// Create the application's GUI and start the application.
new StatBar ("StatBar");
}
}
在使用默认状态栏文字创建了 status JLabel 之后, StatBar 刻画了一个边框 (通过 status.setBorder (BorderFactory.createEtchedBorder ()); )以使状态栏标签与 GUI 的其他部分区别开来。然后,标签被添加到框架窗口内容窗格的南侧区域,这是状态栏的常见位置。
注意 如果不希望状态栏显示任何默认文字,则需要至少输入一个空格来代替文字。如果使用空字符串 ( "" ) 来代替,则不会显示状态栏(虽然会显示其边框)。必须处理状态栏标签的首选字体大小,因为这涉及到当前的字体和那个不可缺少的字符。如果状态栏标签不包含任何字符(空字符串),其首选字体大小就会是 0,造成状态栏无法显示。
MenuItemListener 接口描述了一个 "File" 和 "Edit" 菜单的侦听程序。此接口的 public void menuSelected(MenuEvent e) 方法将在选择这些菜单时被调用,然后会显示菜单的帮助文字。选中一个菜单时,调用 public void menuDeselected(MenuEvent e) 显示文字。
MouseListener 接口描述每个菜单项的侦听程序。其 public void mouseEntered(MouseEvent e) 在鼠标进入一个菜单项时被调用。然后,菜单项的帮助文字会显示在状态栏中。鼠标指针移动到菜单项外时,调用 public void mouseExited(MouseEvent e) ,然后显示默认文字。
每个侦听程序都依赖于 javax.swing.JMenu 或 javax.swing.JMenuItem 的继承 public void setActionCommand(String command) 方法,此方法曾在指定每个菜单或菜单项的状态栏文字时被调用。文字是在侦听程序内部进行检索的,方法是调用相关的 public String getActionCommand() 方法。
抓图程序
几年前,我构建过一个基于命令行的 GetImages 应用程序 -- 请看列表 4 -- 来获取网页中的图像,并将它们存放到我的磁盘上。此应用程序只有一个用于连接到 HTML 文档的 URL 参数。解析 HTML 文档,将 标记的 src 属性值提取为可识别的图像文件,然后下载这些文件。
列表 4 GetImages.java
// GetImages.java
import java.io.*;
import java.net.*;
import java.util.regex.*;
import javax.swing.text.*;
import javax.swing.text.html.*;
import javax.swing.text.html.parser.ParserDelegator;
public class GetImages
{
public static void main (String [] args)
{
// Validate number of command-line arguments.
if (args.length != 1)
{
System.err.println ("usage: java GetImages URL");
return;
}
// Create a Base URI from the solitary command-line argument. This URI
// will be used in the handleSimpleTag() callback method to convert a
// potentially relative URI in an tag's src attribute to an
// absolute URI.
final URI uriBase;
try
{
uriBase = new URI (args [0]);
}
catch (URISyntaxException e)
{
System.err.println ("URI is improperly formed");
return;
}
// Convert the URI to a URL, so that the HTML document can be read and
// parsed.
URL url;
try
{
url = new URL (args [0]);
}
catch (MalformedURLException e)
{
System.err.println ("URL is improperly formed");
return;
}
// Establish a callback whose handleSimpleTag() method is invoked for
// each tag that does not have an end tag. The tag is an example.
HTMLEditorKit.ParserCallback callback;
callback = new HTMLEditorKit.ParserCallback ()
{
public void handleSimpleTag (HTML.Tag tag,
MutableAttributeSet aset,
int pos)
{
// If an tag is encountered ...
if (tag == HTML.Tag.IMG)
{
// Get the value of the src attribute.
String src = (String)
aset.getAttribute (HTML.Attribute.SRC);
// Create a URI based on the src value, and then
// resolve this potentially relative URI against
// the document's base URI, to obtain an absolute
// URI.
URI uri = null;
try
{
// Handle this situation:
//
// 1) http://www.javajeff.mb.ca
//
// There is no trailing forward slash.
//
// 2) common/logo.jpg
//
// There is no leading forward slash.
//
// 3) http://www.javajeff.mb.cacommon/logo.jpg
//
// The resolved URI is not valid.
if (!uriBase.toString ().endsWith ("/") &&
!src.startsWith ("/"))
src = "/" + src;
uri = new URI (src);
uri = uriBase.resolve (uri);
System.out.println ("uri being " +
"processed ... " + uri);
}
catch (URISyntaxException e) {
System.err.println ("Bad URI");
return;
}
// Convert the URI to a URL so that its input
// stream can be obtained.
URL url = null;
try
{
url = uri.toURL ();
}
catch (MalformedURLException e)
{
System.err.println ("Bad URL");
return;
}
// Open the URL's input stream.
InputStream is;
try
{
is = url.openStream ();
}
catch (IOException e)
{
System.err.println ("Unable to open input " +
"stream");
return;
}
// Extract URL's file component and remove path
// information -- only the filename and its
// extension are wanted.
String filename = url.getFile ();
int i = filename.lastIndexOf ('/');
if (i != -1)
filename = filename.substring (i+1);
// Save image to file.
saveImage (is, filename);
}
}
};
// Read and parse HTML document.
try
{
// Read HTML document via an input stream reader that assumes the
// default character set for decoding bytes into characters.
Reader reader = new InputStreamReader (url.openStream ());
// Establish a ParserDelegator whose parse() method causes the
// document to be parsed. Various callback methods are called and
// the document's character set is not ignored. The parse() method
// throws a ChangedCharSetException if it encounters a tag
// with a charset attribute that specifies a character set other
// than the default.
new ParserDelegator ().parse (reader, callback, false);
}
catch (ChangedCharSetException e)
{
// Reparse the entire file using the specified charset. A regexp
// pattern is specified to extract the charset name.
String csspec = e.getCharSetSpec ();
Pattern p = Pattern.compile ("charset=\"?(.+)\"?\\s*;?",
Pattern.CASE_INSENSITIVE);
Matcher m = p.matcher (csspec);
String charset = m.find () ? m.group (1) : "ISO-8859-1";
// Read and parse HTML document using appropriate character set.
try
{
// Read HTML document via an input stream reader that uses the
// specified character set to decode bytes into characters.
Reader reader;
reader = new InputStreamReader (url.openStream (), charset);
// This time, pass true to ignore the tag with its charset
// attribute.
new ParserDelegator ().parse (reader, callback, true);
}
catch (UnsupportedEncodingException e2)
{
System.err.println ("Invalid charset");
}
catch (IOException e2)
{
System.err.println ("Input/Output problem");
e.printStackTrace ();
}
}
catch (IOException e)
{
System.err.println ("Input/Output problem");
e.printStackTrace ();
}
}
public static void saveImage (InputStream is, String filename)
{
FileOutputStream fos = null;
try
{
fos = new FileOutputStream (filename);
int bYte;
while ((bYte = is.read ()) != -1)
fos.write (bYte);
}
catch (IOException e)
{
System.err.println ("Unable to save stream to file");
}
finally
{
if (fos != null)
try
{
fos.close ();
}
catch (IOException e)
{
}
}
}
}
列表 4 使用 javax.swing.text.html.parser.ParserDelegator 类创建一个 HTML 文档解析器,调用解析器对象的 public void parse(Reader r, HTMLEditorKit.ParserCallback cb, boolean ignoreCharSet) 实行解析。此方法使用了三个参数: r 标识一个用于读取 HTML 文档的 java.io.Reader 对象。 cb 标识一个处理所解析的标记及其属性的 javax.swing.text.html.HTMLEditorKit.ParserCallback 对象。 ignoreCharSet 标识是否忽略文档的 标记(如果存在)中的 charset 属性。
解析器在解析过程中调用了各种各样的 ParserCallback 方法。但只有一个方法是 GetImages 需要的:即 public void handleSimpleTag(HTML.Tag t, MutableAttributeSet a, int pos) 方法。这是一个为没有结束标记的标记(例如, )调用的方法,它使用了三个参数: t 通过 HTML.Tag 对象对标记进行标识。 a 通过 javax.swing.text.MutableAttributeSet 对象标识标记内的属性。 pos 标识当前解析到的位置
现在您已经清楚地认识到 GetImages 的工作原理,应该试着运行一下了。例如,您可以指定 java GetImages http://www.javajeff.mb.ca 将该网页上的图像下载到自己的 Web 站点的主页上。结果应该与以下类似(当前目录下出现了一些新图像文件):
uri being processed ... http://www.javajeff.mb.ca/common/logo.jpg
uri being processed ... http://www.javajeff.mb.ca/common/logo.gif
uri being processed ... http://www.javajeff.mb.ca/na/images/wom.jpg
但是 GetImages 在命令行模式下再多么有用,也不及使用 GUI 抓取和查看图像来得方便。我的 IG 应用程序,结合 GetImages 源代码和前面的一些技巧以及一些 GUI 代码正是为此而生。图 4 展示了 GetImages 的 GUI,建议您查看本文的代码归档文件( 资源 )以获取源代码。

图 4 使用抓图程序的 GUI 方便地抓取和查看图像
结束语
此图像抓取程序将文章中介绍的技巧很好地结合在一起,但是还遗留了一些问题,关于这些问题的解决方法就当成家庭作业吧。问题 1:抓图程序下载图像,图像的标记需要指明相对 URL (例如, ),但是无法下载指明绝对 URL 的图像(例如, )。
问题 2:抓图程序无法下载动态生成 的 src 属性的图像。作为例子,以下 标记的 src 属性是我最近在 JavaWorld 主页上发现的: src="http://ad.doubleclick.net/ad/idg.us.nwf.jw_home/;abr=!ie;pos=top;sz=728x90;ptile=1;type=;ord=063423?" 。

作者简介 Jeff Friesen 是一位自由职业的软件开发者,擅长的领域是 C、C++ 和 Java 技术。资源
下载文章中使用的源代码:
http://www.javaworld.com/javaworld/jw-01-2007/games/jw-01-games.zip 您可以使用 Java Fun and Games 中的 DevSquare 在线开发工具来生成和运行 Applet。请阅读此入门指南:
编程百科
2020-08-27 13:06:34
相关推荐: java的初始化 XFire 最新生火指南(上) 继承,构造器,初始化顺序 c++经典面试题 c++面试题 UML的9种图例解析 C++基础知识 Android 之 OO Principle(面向对象的原则)
推荐群组: EXT
更多相关推荐
OO 请教一下分离出导出类对象中的基类对象,不在导出类中添加方法可以实现吗?
编程百科
2020-08-27 13:06:26
相关推荐: java 多线程 生产者-消费问题 Spring service中ThreadLocal的应用? 应该有人需要这个吧——dhtmlxgrid 1.0 professional eclipse 插件与工具下载站点集合Eclipse插件网站分类 Eclipse插件列表 Android 应用和系统优化V1.2 BPEL入门记——Milestone2&3 44个JAVA代码质量管理工具
推荐群组: 读书空间
更多相关推荐
OO 用了code pro studio 觉得太好了。对于改进代码质量很好。最新的版本有个unit test edit ,但是对testng支持不是太好。 用4.6.1 版本,改进了audit 在代码改动时 标记不正常的毛病。
编程百科
2020-08-27 13:06:12
感觉是为了保持javacode的稳定性。这是它的好处。javabean不变,当schema变的时候改配置文件就可以了,不会影响java code。不然像jaxb就要重新generate java code了。
编程百科
2020-08-27 13:06:04
抛出异常的爱 写道
annotation
人造夹层
本来单层的java
又加入了一层
比html标签中加入<%%>还恶心
平白的增加出错机会...不禁要问为什么?
jsp面页难到不是大家用过的最恶心的东西么
灵活么?好用么?美么?

唯一的好处就是大牛门又可以写出如C++般的魔术代码 我们常在页面用JSP2.0的jstl,觉得功能有一些不完善,但也挺好用的啊 也不要一棍子把jsp打死 Annotation用得好,可以减去很多重复的xml配置,举一个例子 属性名与数据库属性字段相同时,用Annotaion可以简洁很多 自己乱说一通,跑题了
编程百科
2020-08-27 13:05:56
public static List getClasses(String packageName, Class clazz) { List res = new ArrayList(); String pckgname = "test.package.test"; pckgname = packageName; String name = new String(pckgname); if (!name.startsWith("/")) { name = "/" + name; } name = name.replace('.', '/'); URL url = ClassTool.class.getResource(name); if (url == null) { return res; } File directory = new File(url.getFile()); if (directory.exists()) { String[] files = directory.list(); for (int i = 0; i < files.length; i++) { if (files[i].endsWith(".class")) { String classname = files[i].substring(0, files[i].length() - 6); try { String clsName = pckgname + "." + classname; logger.debug("clsName=" + clsName); Object o = Class.forName(clsName).newInstance(); // if (o instanceof Class) { // System.out.println(classname); // } if (o instanceof Object) { } res.add(pckgname + "." + classname); } catch (ClassNotFoundException cnfex) { logger.error("getClasses(String)" + cnfex, cnfex); } catch (InstantiationException iex) { } catch (IllegalAccessException iaex) { } } } } return res; } 也许用得到
编程百科
2020-08-27 13:05:48
相关推荐: db4o对象型数据库学习笔记 《DB4O系统应用》--之起步篇 关于db4o中在C/S模式下如何保证对全局共享数据的可靠并发访问 。 七夕表白代码.zip 15个nosql数据库 从类模型转换到数据库表结构的思考 关于数据建模(面向ER)和领域模型建模(面向OO)在企业应用中的作用的讨论
推荐群组: GlassFish
更多相关推荐
OO
关于对象数据库 DB4O 的一些BUG以及如何应对的方法
1、objectmanager 6.0不能正常显示中文而是框框,是字体设置不正确的原因,因为没有源程序,所以无法定位在哪里出了问题。
2、objectmanager 6.0需要对应db4o 6.1的版本
     objectmanager 1.8需要对应db4o 5.5的版本
     objectmanager 1.7需要对应db4o 5.2的版本
版本不对应会造成无法打开数据库文件,怎么就这么不兼容呢?连高版本的管理工具都无法打开旧的数据库。
3、com.db4o.eclipse_0.2.0默认只能打开db4o 5.2的版本的数据库
可以通过修改com.db4o.eclipse.plugin_0.2.0.jar包里的db4o的.jar文件,更换为不同的版本后来访问相应版本的数据库。
先解压 com.db4o.eclipse.plugin_0.2.0.jar 包,到 com.db4o.eclipse.plugin_0.2.0 目录下
方法一:
  把 db4o-5.5-java1.2.jar 或 db4o-6.1-java1.2.jar 等jar文件改名为 db4o-5.0-java1.2.jar ,然后拷贝过来覆盖掉原来的 db4o-5.0-java1.2.jar 文件,重新打开 Eclipse 即可。
方法二:
  拷贝 db4o-5.5-java1.2.jar 或 db4o-6.1-java1.2.jar 等jar文件 到com.db4o.eclipse.plugin_0.2.0 目录下,
  然后进入 com.db4o.eclipse.plugin_0.2.0\META-INF 目录下,打开 MANIFEST.MF 文件,将里面的 db4o-5.0-java1.2.jar 替换成拷贝进来的相应版本的jar文件名,保存,重新打开 Eclipse 即可。
两种方式其实就是替换不同版本的jar文件而已。
4、在用工具打开时有问题时,还需要进入当前用户的目录下将.objectmanager.*之类的文件删除后,再运行工具来打开数据库。
  比如: X:\Documents and Settings\YuLimin 目录下的 .objectmanager.yap之类的文件
编程百科
2020-08-27 13:05:40
相关推荐: Java 专业人士必备的书籍和网站列表 烂译列表 一些适合Java编程的书籍总结 Java程序员必看的 13 本 Java 书籍 Java程序员必读之热门书单 Java开发工程师必备技能 java 入门书籍(java7) JAVA零基础入门书籍推荐
推荐群组: 作书译书圈子
更多相关推荐
OO 对于 Java™ 语言开发人员来说,信息过量是一个真正的问题。每个新入行的程序员都要面临一个令人畏缩的挑战:要进入的行业是一个具有海量知识的行业。要了解的东西简直 太多了。对于有经验的老手来说,情况只有些微好转。知识量总在增大,仅仅跟上进度就是一个挑战。如果有一份专业人士必备的书籍和网站列表该有多好!本文就 是这个列表。它包含了每个专业的 Java 语言程序员在书架或浏览器书签中必备的最重要的书籍和网站。
这些都是您书架上必备的书和应该经 常使用的 Web 链接。时间是一项重要的资源,本文帮您回避那些分心的事情,把时间专注于最有益于您作为Java 语言程序员职业生涯的信息源。尽管有多少程序员就有多少他们最喜欢的参考资料,但本文收集的这些都是优中选优,来源于我书架上的私家珍藏和许多 Java 专家的推荐。
我考虑了两种组织这份参考资料列表的方法。我本可以通过主题领域来组织,这也许很有帮助,但主题列表很快就会变得不实用。相反,我选择了另一种方法:通过类型来组织,即书籍和 Web 站点。
总的来讲,有经验的老手们用 Web 站点来跟踪行业的走势。书籍、文章和论文有助于跟上潮流,但它们总体上更适合于基础学习。极富创造性的书籍偶尔会撼动一两个基础性的东西。这样的书也在本列表之列。
需 要提出的一点警告是,专注于 Java 语言的书籍和 Web 站点数量巨大。您钟爱的未必在这份列表里。那并不意味着它们不好。它们只是不在这份列表里而已。可能是因为我还不知道它们。也可能是因为我不认为它们能够 算得上是重要资源。不包含一些参考资料是一个评判问题,但如果不这样的话,您也许就要花几小时来拖动滚动条,还要花上成千上万美元来买书。如果您作为一个 专业的 Java 程序员,有一些常用的优秀参考资料,一定要让我知道这些资料。这份列表一直都在更新中,您提出的那些也许就会被收录进去。
书籍
每个程序员都会有一些由于经常被当作专业资料参阅而磨坏的书。下列书籍应该是 Java 语言程序员的书架上必备的。书很贵,所以我有意将这份列表弄得很短,仅限于重要书籍。
Thinking in Java (Bruce Eckel)
Thinking in Java, 3rd edition (Bruce Eckel; Prentice Hall PTR,2002 年)
Java 编程思想:第3版 (陈昊鹏 等译; 机械工业出版社,2005 年)
Eckel 的书对于学习如何在 Java 语言环境中使用好面向对象技术极其实用。书中大量的代码样例解释了他所介绍的概念。文字出自一个并不认为 Java 技术总是正确答案的人,所以相当地实用。Eckel 具有多种语言的大量经验,还有用面向对象方式进行思考的扎实技能。本书将这些技能放到实用的 Java 语言环境中。他还在写一本新书,名为 Thinking in Enterprise Java 。
Effective Java (Joshua Bloch)
Effective Java: Programming Language Guide (Joshua Bloch; Addison-Wesley,2001 年)
Effective Java 中文版 (潘爱民 译; 机械工业出版社,2003 年)
本书是理解优秀 Java 程序设计原则的最佳书籍。大多数材料从其他的 “学习 Java ” 的书中根本找不到。例如,Bloch 书中关于覆盖 equals() 这一章是我读过的最好的参考资料之一。他也在书中包括了很实用的建议:用接口替代抽象类和灵活使用异常。Bloch 是 Sun 公司 Java 平台库的架构师,所以他透彻地了解这门语言。事实上,他编写了该语言中大量有用的库。本书必读!
The Java Programming Language (Ken Arnold, James Gosling, David Holmes)
The Java Programming Language (Ken Arnold,James Gosling,David Holmes; Addison-Wesley,2000 年)
Java 编程语言(第 3 版) (虞万荣 等译,中国电力出版社,2003 年)
这也许是能弄到的最好的 Java 入门读物。它并不是一个标准规范,而是一本介绍每门语言特性的可读书籍。这本书在严谨性和教育性方面权衡得很好,能够让懂编程的人迅速被 Java 语言(和其丰富的类库)所吸引。
Concurrent Programming in Java: Design Principles and Patterns (Doug Lea)
Concurrent Programming in Java: Design Principles and Patterns, 2nd edition (Doug Lea; Addison-Wesley,1999 年)
Java 并发编程—设计原则与模式(第二版) (赵涌 等译,中国电力出版社,2004 年)
不是每个开发人员都需要如此细致地了解并发性,也不是每个工程师都能达到本书的水准,但却没有比本书更好的关于并发性编程的概述了。如果您对此感兴趣,请 从这里开始。Lea 是 SUNY 的一名专业程序员,他的和并发性有关的作品和想法都包含在了 JDK 5.0 规范(引自 JSR166)中,所以您大可放心,他所说的关于有效使用 Java 语言的建议是值得一听的。他是一个很善于沟通的人。
Expert One-On-One J2EE Design and Development (Rod Johnson)
Expert One-On-One J2EE Design and Development (Rod Johnson)
WROX: J2EE 设计开发编程指南 (魏海萍 译,电子工业出版社,2003 年)
对于刚接触 J2EE 的人来说,这是唯一的一本如实反映这项技术的书。本书收录了多年的成功经验和失败经验,不同于其他许多作者,Johnson 乐于将失败的经验公诸于众。J2EE 常常都被过度使用。Johnson 的书能帮您避免这一点。
Refactoring (Martin Fowler, Kent Beck, John Brant, William Opdyke, Don Roberts)
Refactoring: Improving the Design of Existing Code (Martin Fowler,Kent Beck,John Brant,William Opdyke,Don Roberts; Addison-Wesley,1999 年)
重构:改善既有代码的设计(中文版) (侯捷 等译,中国电力出版社 ,2003 年)
Fowler 写了几本现已出版的最流行的编程书,包括 Analysis Patterns 。他的关于 重构 的书是这一主题的基本书籍。重构代码是被程序员忽略的训练,但却是程序员最直观的想法。重构是在不改变代码结果的前提下改进现有代码的设计。这是保持代码 整洁的最佳方式,用这种方法设计的代码总是很容易修改。什么时候进行重构呢?当代码“散发出味道”时。Fowler 的书里满是 Java 语言代码的例子。许多 Java 语言集成开发环境(IDE)(包括了 IBM 的 Eclipse)都将 Fowler 的重构包含了进去,每一个都使用他的重构名命名,所以熟悉如 extract method 等重构方法还是很值得的。
Design Patterns (Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides)
Design Patterns: Elements of Reusable Object Oriented Software (Erich Gamma,Richard Helm,Ralph Johnson,John Vlissides; Addison-Wesley,1997 年)
设计模式:可复用面向对象软件的基础 (李英军 等译,机械工业出版社 ,2005 年)
这是一本在专业程序员圈子里更为有名的书,基于作者共同的绰号,这本书被认为是 “四人帮(GOF)之书”。模式是思考和解决普通编程问题时可以重用的方式。学习模式是一门学科。使用好模式(或知道什么时候 不 使用模式)是一项技能。忽略模式则是错误的。书中所有的例子都以 C++ 表示,但 Java 语言是从那里诞生的,让 Java 语言程序员由此联系到如何在 Java 语言中实现这些模式相对简单一些。熟悉模式并了解如何使用好模式使编程更加简单。这使得和其他程序员交流也更简单,因为在针对通用问题的通用解决方案中, 模式是描述解决方案中彼此协作的大量相关编程概念的快捷方式。一些更为通用的方式,如 工厂方法 则是普便存在的,甚至存在于 Java 语言本身。关于明智使用模式的这个主题,也可以阅读 Joshua Kerievsky 的 Refactoring to Patterns ,该书称可以让代码来告诉您何时实现模式。
Patterns of Enterprise Application Architecture (Martin Fowler)
Patterns of Enterprise Application Architecture (Martin Fowler; Addison-Wesley,2002 年)
企业应用架构模式 (王怀民 等译,机械工业出版社 ,2004 年)
比起小型、一次性项目来说,企业开发当然代表了更大的挑战。那并不意味着企业开发带来的所有挑战都是新挑战。事实上有些时候,这项开发 已经 是以前完成过的了。Fowler 做了很多个这样的项目。他的书提到了一些通用解决方案,并提供了关于使用、折中和可选方案的指导。Fowler 在书中包含了一些熟悉的模式,如模型视图控制器(MVC),他也提供了一些您也许不了解的模式,如处理 Web 站点上特定页面请求或行为请求的 Page Controller 模式。正如您对待大多数模式一样,一旦您读过许多模式,您就会认为 “我已经知道那个模式了” 。也许是这样,但有一个用来引用模式的通用表达方式还是很有帮助的。在有多个组件(由不同人开发)的大型项目中,该类引用是一项很好的帮助。
UML Distilled (Martin Fowler)
UML Distilled: A Brief Guide to the Standard Object Modeling Language (Martin Fowler; Addison-Wesley 2003 年)
UML精粹:标准对象语言简明指南(第3版) (徐家福 译,清华大学出版社 ,2005 年)
对于专业的程序员来说,UML 是一门很重要的通用可视化沟通语言,但是它被过度使用和草率地滥用了。您无需对使用 UML 沟通了解太多。Martin 对 UML 的提炼为您提供了最核心的东西。事实上,前后的封页提供了常规基础上可能使用到的所有东西。该书中 UML 例子的代码都是 Java 代码。
Test-Driven Development: By Example (Kent Beck)
Test-Driven Development: By Example (Kent Beck; Addison-Wesley 2002 年)
测试驱动开发(中文版) (崔凯 译,中国电力出版社 ,2004 年)
测试优先编程将使编程发生革命性变化,能助您成为更好的程序员。在写代码之前编写测试开始很难,但却是一项威力强大的技能。通过优先编写测试,可使代码更 加简单,并确保从一开始它就能工作(Beck 实践着他提倡的测试优先,与人合写了 JUnit,这是 Java 语言最流行的测试框架)。Beck 的书是权威的参考资料,扩展了的 Money 例子也用 Java 语言写成。Beck 详述了如何用测试优先进行 思考 (这也许是许多程序员首先遇到的障碍)。
The Pragmatic Programmer: From Journeyman to Master (Andy Hunt and Dave Thomas)
The Pragmatic Programmer: From Journeyman to Master (Andrew Hunt 和 David Thomas; Addison-Wesley 1999 年)
程序员修炼之道——从小工到专家 (马维达 译,电子工业出版社 ,2004 年)
做一个纯粹的面向对象开发人员有其优势所在。在当今复杂的社会中,作为 Java 语言开发人员,为完成任务常要妥协。Hunt 和 Thomas 探讨了如何不将真正重要的东西妥协掉而完成任务。这不是一本关于 Java 语言的书,而是 Java 语言开发人员重要的思想读物。例如,我认为没从“要解决问题,而不是推卸责任”这句忠言中受益的程序员,不能像个自豪的艺术家一样在他的杰作上签上大名。
Peopleware: Productive Projects and Teams (Tom DeMarco and Timothy Lister)
Peopleware: Productive Projects and Teams (Tom DeMarco,Timothy Lister; Dorset House,1999 年)
人件(第2版) (UMLChina 翻译组 译,清华大学出版社 ,2003 年)
这份列表中的其他所有书籍都至少和技术有些相关。这本书却不是。在所有技术行话和首字母缩略词的海洋中,有时软件开发人员和经理们会忘记:是 人 制造了软件。DeMarco 和 Lister 向我们提醒了这一事实,也向我们提醒了形成这一区别的原因。这不是一本关于一门特定编程语言的书籍,但却是每个 Java 语言程序员都应该读的书。关于 “累死程序员如何让经理们适得其反” 还有许多其他的好书,但这是最好的一本。


回页首

Web 站点
Web 站点的数目浩如烟海,如果您想要消化其中的内容,穷毕生之力也难以全部访问。包含 Java 语言某方面内容的详尽的网站列表会大得离谱。下列站点都是可靠、真实的。
Sun 的 Java 技术站点
Sun 的 Java 语言站点
这是 Sun 的 Java 语言主站。作为 Java 语言开发人员,您会发现自己频繁地访问此站点。下列链接特别重要,特别是对新入行的 Java 语言开发人员: New to Java Center
New to Java Center
New to Java Center 存放了许多循序渐进的 Java 技术资源链接。如果您刚接触这门语言,这是一个好的起点。 教程和代码库
Java Tutorial
这里有大名鼎鼎的 Java Tutorial,以及关于 Java 语言各个方面(例如 Collection)的其他教程。
IBM developerWorks
IBM 的 developerWorks
推销自己也许有些厚脸皮,但 developerWorks 是一项巨大的资源,收录了大量 Java 语言工具和技术的教程和文章。其内容从初学者指南到学习这门语言到高级并发性技术。可以根据主题搜索内容,然后根据类型浏览。
Apache Software Foundation
Apache Software Foundation
Apache 站点是许多可重用库(通用领域)和工具的主页,这些库和工具帮助 Java 开发人员进行开发。这里的内容全都是开放源码,所以尽管下载想要的吧!许多极其流行的 Java 语言库和工具(如 Struts、Ant 和 Tomcat)都始于 Apache 项目。Jakarta 专区汇聚了大多数新兴的 Java 语言材料。
Eclipse.org
Eclipse
有几个好的 Java 语言集成开发环境(IDE)。Eclipse(来自 IBM)是最新的 IDE 之一,它很快成为 Java 语言开发的首要 IDE。它完全是开源的,这意味着它是免费的。该站包含了学习如何有效使用 Eclipse 的各种参考资料。这里还有关于 Standard Widget Toolkit(SWT)的信息,SWT 是相对于 Swing 来说更加轻量级的选择。
Eclipse 插件中心和 Eclipse 插件
Eclipse 插件中心 和 Eclipse 插件
Eclipse 基于插件架构。事实上,插件是 Eclipse 的 Java 语言开发组件。但有差不多上千个插件,从 Web 开发的插件到在 Eclipse 环境中玩游戏的插件。这两个站点分类列出了大多数插件,可以进行搜索。它们是很棒的资源。如果您想在 Eclipse 开发环境中弄点新东西,幸运的话有某个插件可能已经实现,从这两个站点能找到想要的插件。这两个站点都允许评论插件,这样您就可以知道哪些插件好,哪些值 得一试。
JUnit.org
JUnit.org
Junit 是 Java 语言中一个基本的单元测试框架。该站点包含了 Junit 最新最棒的版本,外加大量有关测试(Java 语言或者其他语言的)各个层面上(针对桌面应用程序、Web 应用程序、J2EE 应用程序等)的其他资源。如果您想找测试资源,这里就是最佳起点。
TheServerSide.com
TheServerSide.com
如 果您要(或将要)从事服务器端 Java 语言的开发,此站点是一处举足轻重的资源。您可以到这里找到有关 JBoss、J2EE、LDAP、Struts 和大量其他主题的文章,并且都是完全可检索的。这些文章不仅仅是简单描述 Java 语言的特征或者支持的库。它们更进一步地描述了库的新奇用法(如使用 Jakarta Velocity 作为规则引擎,而不是模板引擎)。它们也提供了有关 Java 语言现状的连续评论(当前的一篇文章是由 Tim Bray 所写的 Java is boring )。该站点更好的通用功能之一是对 Java 语言工具和产品(应用服务器等)的矩阵式比较。
Bruce Eckel's MindView, Inc.
Bruce Eckel's MindView, Inc.
Eckel 写了几本 “用 …… 进行思考” 的书,内容关于 Java 语言、Python 和 C++ ,当我学习 Java 语言时,他的 Thinking in Java 对我尤其有帮助。它很实用并切中要害,在“在 Java 语言环境中如何面向对象思考”方面具有卓识。您可以从此站点免费下载他所有书籍的电子版。他也写了许多好文章,并且他把这些文章的链接都放到了这里(包括 关于 Jython、Java 和 .NET 比较等内容的文章)。
ONJava.com
ONJava.com
O'Reilley 历年来出版了一些有关编程语言和工具的优秀书籍。他们的专注于 Java 语言的网站也不错。它有些有关各种 Java 语言工具(如 JDOM 和 Hibernate)、Java 平台(如 J2SE 和 J2EE)不同领域不同部分的文章。全部都可以被检索到。他们有优秀的文章和教程。该站点按主题排列。例如有 Java 和 XML、Java Security、Wireless Java 和 Java SysAdmin。该站点也有到 O'Reilley Learning Lab 的链接,在那里您能获得在线参考资料(Java 语言相关和其他的)。那些不是免费的,但是许多都面向大学认证。因此您可以以一种很方便的方式来学习技能,并得到一些认证。
java.net
java.net 社区
java.net 社区有多个“社区”,有特定于主题的论坛和文章。例如 Java Desktop 社区有各类与 Java 语言桌面开发相关的资料。Java Patterns 社区作为一个门户,也许对提供 Java 语言的模式资源相当感兴趣。还有一个 Java User Groups (JUG) 社区,在那里能找到有关创建、加入和管理一个 JUG 的信息。

 
回页首

结束语
任 何 “好的”、“关键性的” 或者 “重要的” 参考资料列表都注定是不完整的,本文的列表也未能例外。 Java 语言的书籍数目众多,当然,万维网也很庞大。除本文所列的参考资料之外,还有很多用于学习 Java 语言的参考资料。但如果您拥有了这里所提到的所有书籍、网站、文章或者教程,您应当已经拥有了一个使您良好开端并助您登堂入室的实用宝库。
最 后,要成为一个能力日增和高效的 Java 语言开发人员,方法就是用它工作,动手来尝试。如果有一个教程详细介绍了所需创建的软件的每一部分,您很可能并没得到多少好处。有时,您可能得走自己的 路。在成功地尝试了一些新的东西之后,您可能想要写一篇文章、教程或者一本书来分享您所学到的。
编程百科
2020-08-27 13:05:31
相关推荐: JavaScript内核系列 第1章 前言及概述 web UI技术综述 flex与JAVA的SOCKET通信 12.Java内存模型 【游戏设计模式】之四 《游戏编程模式》全书内容提炼总结 应用集成与数据集成建设总体思路 中间件技术及其应用
推荐群组: springMVC
更多相关推荐
OO 本文来自 http://hi.baidu.com/yuxichu/blog1. Web开发技术的困境 1.1 JSF/ASP.net的缺陷 JSF/ASP.net在Web编程技术的发展中是一个了不起的进步,无数赞美的文章以及成功的案例足以说明这一点。但遗憾的是,JSF/ASP.net虽然实现了面向对象的Web编程,却在提供高效的Web应用系统方面做了倒退。在典型的JSF/ASP.net程序中,每一次Web事件的处理都需要将整个表单的数据发送到服务器端,由Web服务器重建该表单的服务器端视图,再调用相应的事件处理函数进行处理。这种处理方式利于实现代码与界面的分离和面向对象编程, 但却对系统性能造成了严重的影响,主要的缺陷有: 1. 所有的表单数据(包括表单的状态视图数据)都要发送到服务器上,整个页面需要刷新, 无法做到按需传递数据,在客户端与服务器之间频繁进行这种大量数据的传送将严重占用网络带宽; 2. 在当前客户机的性能越来越好的环境下,却将所有的业务逻辑都放在服务器端执行, 甚至简单的调整显示界面的代码都放到服务器端执行,这是对客户端资源的严重浪费, 简直可以用“暴殄天物”来形容; 3. 虽然经历了种种努力,在JSF/ASP.net编写的稍为复杂的Web程序中其界面描述部分(如JSF中的jsp文件和ASP.net中的aspx文件)中仍然做不到完全消除JavaScript代码(这些代码出现在或html标签的onEVENT属性中)。这样,JSF/ASP.net实际上没有完全实现界面与代码分离的目标。 4. 必须使用客户端脚本时,很难以直观的方法使 JavaScript代码也能以面向对象的方式进行编程。 虽然,某些JSF实现方案可以与AJAX结合起来使用,但这只在一定程度上解决了客户端与服务器之间数据交换的问题, 却使得整个编程模型更加晦涩难懂,而且AJAX并不是一个真正的面向对象编程模型。 1.2 一个常见的小“错误” 在分析了JSF/ASP.net技术的缺陷之后,为了便于描述本文即将提出的单一环境Web编程模型,在这里还得讲述一下我们很多人在刚学Web编程时都可能犯过的一个小“错误”──偶尔误解了代码的运行环境。在一个ASP/JSP文件中,其内容常常是一团各种内容的混杂体:各种HTML标签用于描述界面、客户端中包含着在浏览器中执行的代码、 <% %>之间则嵌着服务器端代码。这些内容混杂在一起,还经常必须通过服务器端代码来生成客户端代码。由于它们是如此的混乱,以致于很多人都犯过混淆代码运行环境的小“错误”,如例1-1所示。 例1-1 在ASP中显示错误消息(错误的形式) 在例1-1中,其意图是通过alert函数将服务器端的错误消息显示给用户, 却没有注意到无法在服务器端直接修改客户端变量的值。其正确形式应该是这样的: 例1-2 在ASP中显示错误消息(正确的形式) 但例1-2中的表示方式却不符合我们的思维习惯,如果错误描述中包含有单引号(')或回车换行符(\r\n), 就特别容易发生错误。当这种要求反复出现时,常常会给程序员带来沉重的负担。 在ASP/JSP程序中经常发生的这类“错误”说明,我们的大脑习惯于连续的思考同一个问题。在理想的情况下,一次业务或事件处理过程应该视为一个连续过程,而在传统的Web编程环境中,程序员必须将这个过程划分为多个阶段,其中一部分发生在客户端、而另一部分发生在服务器端,同时还得考虑客户端与服务器端之间的连接过程,于是一个连续的过程就被人为切割为多个片断,同时还得考虑各个片断的运行环境以及片断之间的影响。由于这类划分不是合乎逻辑的自然划分,从而对程序员的大脑提出了严重的挑战。当任务繁重时,人们很容易因为疏忽发生不易查觉的错误, 从而为系统的安全与稳定埋下严重的隐患。 2. 单一环境Web编程模型 2.1 概述 由于JSF/ASP.net在性能方面存在重大缺陷,AJAX则无法实现真正的面向对象编程(关于AJAX的不足之处已有很多文章予以描述,为免累赘本文不再重述),而在Web程序运行环境中,由于客户端与服务器的区分迫使程序员必须对一个业务处理过程进行人为的划分,同时这种划分导致各部分代码之间的存在相当微妙的影响,这就极易击中我们的思维盲区,因此本文顺应例1-2中描述的“小错误”,将错就错,提出单一环境Web编程模型。 该模型的基本思想是,为应用程序员营造一个虚拟的单一运行时环境,使得程序员可以将用户界面的事件处理逻辑编写为一个单一的处理过程, 不需区分哪些是客户端脚本,哪些是服务器端代码;然后由编译器将这个单一处理过程编译为适当的客户端脚本和服务器端代码, 并在适当的位置自动添加处理客户端脚本与服务器端代码之间交互的代码。 为便于理解,下面将以一个简单的示例来演示单一环境Web编程模型的实现机制。 该示例的功能为:点击页面上的一个按钮,则弹出一个模态对话框,向用户报告服务器上的当前时间。为便于与已有的Web编程技术进行比较, 该示例分别用ASP.net和单一环境Web编程模型描述一遍。 首先,以ASP.net描述,由两个文件组成:ServerTime.aspx和ServerTime.aspx.cs,分别如下: 例2-1 以ASP.net编写的显示服务器时间的用户界面部分 -----------------------------------ServerTime.aspx--------------------------------------- <%@ Page language="c#" Codebehind="ServerTime.aspx.cs" AutoEventWireup="false" Inherits="test.ServerTime" %>        ServerTime           
         
   例2-2 以ASP.net编写的显示服务器时间的代码部分 -----------------------------------ServerTime.aspx.cs--------------------------------------- namespace test {   public class ServerTime : System.Web.UI.Page   {     protected System.Web.UI.WebControls.Button BtnShowServerTime;     override protected void OnInit(EventArgs e)     {       InitializeComponent();       base.OnInit(e);     }     private void InitializeComponent()     {       this.BtnShowServerTime.Click += new System.EventHandler(this.BtnShowServerTime_Click);     }     private void BtnShowServerTime_Click(object sender, System.EventArgs e)     {       //生成消息       String msg=DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");       //生成在客户端显示消息的脚本,其中脚本内容在浏览器上执行       this.RegisterClientScriptBlock("X","");     }   } } 接下来,以单一环境Web编程模型描述,同样也由两个文件组成:ServerTime.uw和ServerTime.uw.code,其中ServerTime.uw.code使用类C#语法编写, 其内容分别如下: 例3-1 以单一环境Web编程模型编写的显示服务器时间的用户界面部分 ------------------------ServerTime.uw-------------------------------------------        ServerTime           
           
   例3-2 以单一环境Web编程模型编写的显示服务器时间的代码部分 ------------------------ServerTime.uw.code------------------------------------------- namespace test {   public class ServerTime   {     //为表单Form1声明一个按钮对象     Button Form1.BtnShowServerTime;     //注册对象的事件处理函数     public ServerTime()     {       Form1.BtnShowServerTime.onclick=BtnShowServerTime_Click;     }     private void BtnShowServerTime_Click()     {       //生成消息,该部分实际上在服务器上执行       string msg=DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");       //显示消息,该部分实际上在浏览器上执行       window.alert(msg);     }   } } 比较例2与例3中的事件处理过程,可以看到二者之间的明显差异: 1. 在ASP.net中,表单中的所有数据都必须提交给服务器,当表单中数据项特别多或状态视图(StateView)数据量很大时,这种多余的数据传输将多消耗大量网络资源;而在单一环境Web编程模型中,编译器通过分析代码,将其分离为服务器执行部分与客户端执行部分,通过类似于AJAX技术的方式将服务器端代码必需的数据提交给服务器,并获得服务器生成的消息,在这过程中,没有非必需数据的传输,可以极大的节省网络资源。 2. 在ASP.net中,客户端代码是通过字符串连接方式生成的,实际上编译只将其作为普通的字符串处理, 这样实际上就不能使用编译时的语法检查,失去了一个发现、修改错误的环节; 而在单一环境Web编程模型中,编译器可以对代码进行全面的语法检查。 3. 在ASP.net 中,显示消息的方式不符合人类的思维习惯, 该过程被人为划分为客户端与服务器两个阶段,并且这种个阶段的代码交织在一起,另一方面当消息格式复杂时,生成正确语法脚本的难度也大大增加,降低了系统的健壮性;而在单一环境Web编程模型中,消息以一种自然的方式(类似于桌面编程的方式)向用户显示, 消息的内容也不会影响代码的正确性。 4. 在单一环境Web编程模型中,界面描述部分是一个纯HTML页面,其中不含任何脚本代码, 实现了代码与界面的完全分离。 2.2 实现机制 通过上面示例的演示,本文设想单一环境Web编程模型的实现机制如下: 每一个Web表单由两部分构成,其中界面描述部分是一个纯HTML文件,该文件中通常不包含任何脚本代码(实际上允许嵌入脚本代码,但不鼓励这么做); 事件处理的代码部分全部位于代码文件中,该文件内容以类C#语法编写,并且将客户端与服务器的运行时环境视为一个整体,在代码中不用明确处理二者之间的差异。 为了更好的解释单一环境Web编程模型的实现机制,本文给出代码文件的设计示例, 后面的讨论基于这种文件格式进行。为了便于理解与对比,采用类C#语法描述,其基本结构如下: import JavaScript.js;//被引用的JavaScript.js文件 using PackageName;//被引用的服务器端的命名空间或包 namespace Namespace {   class ClassName   {     //页面级变量声明段     client data_type1 client_variable; //客户端变量     server data_type2 server_variable; //服务器变量     media data_type3 media_variable;  //中介变量     client Button FormName.BtnOK; //用户界面元素     //初始化函数     Init()     {       //完成显示界面对象的事件与处理逻辑的映射关系       FormName.BtnOK.onclick=BtnOK_OnClick; //映身一个点击事件处理逻辑     }     //事件处理函数     void BtnOK_OnClick()     {       //完成访问数据库、调用其他服务器端程序、以及改变显示界面等工作     }     //其他辅助函数     data_type function_name(data_type1 var1, … )     {     }   } } 1. 关键词import 导入一个脚本文件,其功能类似于< script language="JavaScript" src="path.js">,从而在该代码文件中可以直接调用该脚本文件中声明的函数。每一个import语句都被编译为一个脚本引用块,形如:。 2. 关键词using 允许在命名空间中使用类型,以便在命名空间中使用类型而不必指定命名空间, 其意义与C#中的using指令及JAVA中的import相同。 3. 关键词namespace 声明该类所属的命名空间或包,其意义与C#中的namespace及JAVA中的package相同。 4. 关键词class 声明一个类,其含义与C#及Java中的class类似。不同之处在于,此处声明的“类”可分为四种类型:一、类中没有服务器端变量与方法,且有客户端变量或方法,则该类为客户端类, 这种类型的class被编译为客户端脚本;二、类中没有客户端变量与方法,且有服务器端变量或方法,则该类为服务器端类, 这种类型的“类”被编译为服务器端类,如C#类或Java类等;三、类中既有服务器端变量或方法,也有客户端变量或方法,则该类属于混合类,这种类型的类中的一部分内容被编译为客户端脚本,另一部分内容被编译为Web服务,并且由编译器自动生成客户端脚本调用Web服务的代码以处理二者之间的交互。四、类中既没有服务器端变量或方法,也没有客户端变量或方法,则该类属于中性类, 这种类型的类被同时编译为客户端脚本和服务器端的代码。 5. 关键词client、server、media 这三个关键词均用于指定变量的性质,其中: client——声明一个仅用于客户端的变量,这类变量通常是显示界面的元素,也可能是ActiveX对象或简单数据类型变量,另外还包括默认的window、event、document、location、status等页面对象; server——声明一个仅用于服务器端的变量,这类变量通常是数据库连接对象、业务处理逻辑对象实例等, 也可能是简单数据类型变量,另外还包括默认的Request、Response、Session、Application、Server等服务器端对象; media——声明一个中介变量,这类变量用于在服务器与客户端变量之间交换信息, 其数据类型必须是简单数据类型,即char、integer、number、float、boolean、date-time等数据类型及其数组。 为了减轻程序员的负担,这三个关键词是可以省略的。 在这种情况下,编译器根据变量的数据类型确定其性质,其中: 数据类型为界面对象或ActiveX对象的变量为client变量; 简单数据类型及其数组为media变量;其他均为server变量。 6. 函数 类中的函数根据其引用的变量和调用的函数的性质可以分为四类:一、客户端函数,引用了客户端变量或调用了客户端函数,但没有引用服务器变量且没有调用任何服务器函数,这类函数被编译为JavaScript脚本函数;二、服务器函数,引用了服务器变量或调用了服务器函数,但没有引用客户端变量且没有调用任何客户端函数, 这类函数被调编译为服务器端代码表示的函数;三、中性函数,没有引用服务器变量且没有调用服务器函数,也没有引用客户端变量且没有调用任何客户端函数,这类函数被同时编译为等价的JavaScript脚本函数和服务器端代码。 四、混合函数,除前面三类以外的其他函数,这类函数被编译为一个客户端的JavaScript函数和一个或多个的Web服务方法, 并由编译器自动添加代码处理它们之间的交互。 6.1 初始化函数Init() 该函数属于混合函数,其主要功能是完成显示界面元素的事件与处理逻辑的映射关系, 被编译器编译为一段直接执行的客户端脚本,插入在编译后的页面末尾, 即之前。 根据混合函数的定义,初始化函数也可以引用服务器端变量或调用服务器对象的方法, 但这种使用方式是不被鼓励的。 6.2 事件处理函数 该类函数属于混合函数,其主要功能是对显示界面上产生的事件做出响应, 完成界面的改变、业务逻辑的调用等。 该类函数总是被编译为一个客户端的JavaScript函数和一组Web服务方法(0个或多个)。 6.3 辅助函数 该类函数的性质根据函数分类规则,由编译器决定,并按照其性质进行编译。 处理完代码文件后,只需将生成的JavaScript块插入到界面描述文件的末尾,即之前,其顺序为:import语句引入的脚本文件、页面全局变量的声明、辅助函数、事件处理函数、根据初始化函数生成的脚本代码, 最后将转换后生成的界面描述页面保存为一个静态html文件。 2.3 混合函数的编译规则 由于JavaScript、Java、C#语法的相似性,根据前面对单一环境Web编程模型中代码文件格式的描述, 可以发现中性函数、服务器端函数、以及客户端函数是非常容易编译的, 因此下面只讨论难度最大的任务,即对混合函数的编译。 根据定义可知,混合函数的调用总是由客户端发起的 (页面加载时、某个键盘/鼠标事件的发生、定时器到时等),且只能被客户端函数或混合函数调用。为了完成对混合函数的编译,首先需要对函数语句进行分段,使得每一段内都不同时包含客户端变量或函数和服务器端变量或方法。如例3-2中的事件处理函数BtnShowServerTime_Click为例,可以如下分段: 表1 混合函数分段 段类型 代码 客户端段(1) //空 服务器段 //生成消息,该部分实际上在服务器上执行 string msg=DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"); 客户端段(2) //显示消息,该部分实际上在浏览器上执行 window.alert(msg); 为便于处理,假设函数中每一个语句都不同时包含客户端对象(变量与函数)和服务器端对象 (指变量与方法)── 通过引入更多的中间变量,总可以使函数满足这一假设。 这样我们总能如表1所示将一个函数划分为多个互不重迭的段,然后对混合函数进行以下处理: 1. 生成一个与混合函数同名的JavaScript函数,其开始部分是所有media和client变量的声明; 2. 将客户端段的代码直接复制到JavaScript函数中;并分析该段代码,获得被修改过的media变量清单, 在函数中加入将该清单中的变量“序列化”为一个字符串s的代码; 3. 如果没有下一段,则结束,否则下一步; 4. 在JavaScript函数插入调用Web服务的代码,其中参数为第2步生成的字符串s; 5. 为服务器段生成一个Web服务的方法M,其接受的参数即为第2步生成的字符串s; 6. 为Web服务的方法M生成还原media和server变量的代码,以及根据输入参数修改的media变量的代码; 7. 将服务器段的代码复制到方法M中;并分析该段代码,获得被修改过的media变量清单, 在方法中加入将该清单中的变量“序列化”为一个字符串s的代码和暂存该方法中media和server变量的代码, 并将字符串s作为方法M的返回值; 8. 在客户端JavaScript函数中加入Web服务调用结果处理的代码, 生成根据返回结果修改media变量值的代码; 9. 如果没有下一段,则结束,否则转步骤2,继续处理后续的客户端段和服务器段。 在编译混合函数时,其他需要处理的问题主要有两个: 1. 页面级的服务器端变量的暂存问题。一种可行的方案是,根据这些变量是否可序列化分别进行处理, 其中可序列化的变量暂存到在持久的存储介质(如磁盘)中,不可序列化的变量暂存到Session中。 2. 过程级的服务器端变量的暂存问题。一种可行的方案是,仿照Session的管理方式,建立一个Transact管理机制,当一个事件的处理函数包含多于一个服务器段时,为了在段与段之间保持变量的值,可以将服务器端变量暂存到Transact中,该Tranact在第一个服务器段执行前建立,并在最后一个服务器段结束后销毁。 3. 总结 根据前文的描述可以知道,建立一个单一环境Web编程模型在技术上是可行的。 以该编程模型进行Web开发的优点主要有: 1. 与桌面编程模型一致 从前面的事件处理示例的描述可以看出,编译系统通过屏蔽客户端与服务器之间的差异, 为程序员建立了一个单一的“运行时环境”,因此采用单一环境Web编程模型进行Web开发时,其形式与用VB、C#、Java等进行桌面系统开发非常相似,程序员不用处理客户端与服务器环境的差异, 大大减轻了程序员的负担。 2. 与传统的网页设计技术一致 在单一环境Web编程模型中,界面描述文件是一个纯HTML文件, 开发人员只需掌握HTML页面设计技术即可,无需学习其他附加的东西;而代码文件实际上是JavaScript代码与C#/JAVA代码的混合体, 由于JAVA、JavaScript、C#三者语法的相似性,采用JavaScript、JavaScript、C#的开发人员无需学习新的语法规则就可直接以该模型进行编程,编写事件处理函数与编写一个常规的JavaScript函数一样容易。 3. 实现了代码与页面的完全分离 在单一环境Web编程模型中,界面描述文件是一个纯HTML文件,可以做到完全不包含任何脚本内容,所有的事件处理代码(无论是完全在客户端执行的还是需要客户端与服务器协同执行的) 都置于同一个代码文件中,从而做到了代码与页面的完全分离。这样做的好处是,页面设计人员与代码编写人员可以互不干扰的进行各自的设计,并且都可以完全控制自己处理的那部分工作,不用彼此担心无意中破坏了对方的工作。 4. 真正的面向对象编程 现在主流的浏览器都支持DHTML技术,而DHTML技术所使用的DOM模型(文档对象模型)本身就是一个非常好的对象体系。单一环境Web编程模型利用DOM作为其用户界面描述的对象模型, 从而完全继承了DOM的面向对象特性。基于此,每一个熟悉DHTML的编程人员不用学习新知识即已掌握单一环境Web编程模型中使用的对象模型。 5. 融合Ajax与Web服务技术于无形 Ajax 技术的优点在于可以只向服务器提交必要的信息,并且避免了页面的刷新; 而Web服务技术的优点在于只接收必要的输入参数,并返回处理的结果。于是,二者的结合可以最大限度的降低浏览器与服务器之间的数据转输量,并且服务器不用执行与显示相关的处理工作,从而最大限度的利用了浏览器的计算能力。在单一环境Web编程模型中,事件的处理函数通过编译器进行重新组织后,每次向服务器只提交必要的数据,并且将更新页面显示效果的代码完全移到了浏览器上执行,这样就以一种对应用程序员透明的方式实现了Ajax与Web服务的优点, 充分利用了客户端机器的计算能力。 6. 支持一个页面上提供多个表单 在ASP.net中,由于技术上的限制,一个页面只能包含一个服务器端的表单, 这种人为的限制非常不利于程序员对用户界面进行逻辑划分;但在单一环境Web编程模型中不存在类似的限制,一个页面上可以包含多个表单, 并且在一次事件处理过程中,可以同时访问多个表单中的对象。 7. 学习代价小 单一环境Web编程模型对应用程序员没有引入额外的编程规范,同时由于JavaScript、C#、Java语法的相似性,其代码语法可以采用类C#语法。因此,在这样一个环境中进行Web开发,编程人员不用学习其他额外的知识。 由于DHTML、JavaScript、C#(对.Net程序员)、 JAVA(对Java程序员)对每一个Web编程人员来说都是必须掌握的,与JSF/ASP.net相比较,采用单一环境Web编程模型进行开发,所必须学习的知识是最少的, 并且所学习的知识都是Web开发的基石 ── 变化少且是必须的, 而JSF/ASP.net中的那些服务器端控件和自定义控件或标签都是最终要还原为DHTML、JavaScript、C# (或Java)代码的,并不是Web开发中必不可少的。 Web开发人员花在学习非必要知识上的时间越少,用在真正有价值工作的时间才会越多。 8. 提高了开发效率 与已有的Web编程技术相比,在单一环境Web编程模型中, 客户端变量与服务器变量之间可以“直接”交换信息,服务器端代码不必通过串接字符串生成动态的JavaScript脚本(如例3-2所示),因此可以大大降低代码的编写难度,从而降低了代码中出现BUG的机会,提高了开发效率。 正所谓寸长尺短,单一环境Web编程模型在为应用程序员营造了一个虚拟的单一环境的同时也潜伏了一个“诱人”的陷阱,如果应用程序员在进行Web开发时没有意识到这个单一环境只是“虚拟的”而非与真正象桌面编程的运行环境一样,不清楚代码的实际运行环境和系统在后台完成的辅助工作, 也很有可能编写出如例4所示的低效代码。 例4 一段低效的单一环境Web编程模型代码 //table是通过数据库获取一个数据表单System.Data.DataTable变量 int j=table.rows.Count; for ( i = 0; i < j; i++) {   //获得代码与名称   string code = table.rows[i][0].ToString();   string name = table.rows[i][1].ToString();   //向下拉列表添加代码与名称对   Option o = document.createElement("OPTION");   Form1.ProductsList.options.add(o);   o.value = code;   o.text = code; } 对例4中的代码,单一环境Web编程模型的编译器实际上会如表2所示对它进行分割。 表2 例4中代码的分段方式 段类型 代码 客户端段(1) //空 服务器段(1) //查询数据库获得DataTable //获得DataTable中记录的行数 int j=table.rows.Count; 客户端段(2) for ( i = 0; i < j; i++) { 服务器段(2) //获得代码与名称 string code = table.rows[i][0].ToString(); string name = table.rows[i][1].ToString(); 客户端段(3) //向下拉列表添加代码与名称对 Option o = document.createElement("OPTION"); Form1.ProductsList.options.add(o); o.value = code; o.text = code; } 服务器段(3) //空 客户端段(4) //空 其中最糟糕的部分在于服务器段(2)被嵌入在一个循环中,这在实际执行时将导致多次的Web服务调用,消耗大量的网络资源,并可能造成用户界面失去响应相当长一段时间。 因此为了提高系统效率,在一个事件处理函数中只应包含0或1个服务器段,即在一次事件处理中最多只发生一次Web服务调用。 涉及网络的编程从来不是一件轻松事,即使在单一环境Web编程模型中, 虽然程序员可以不处理客户端环境与服务器环境的区别及二者之间的交互,但仍然需要考虑网络对程序性能的影响。虽然良好的工具可以降低我们的工作强度,但复杂的事情永远都是复杂的事情,它所涉及的某些因素可以因为工具的进步被隐藏起来, 却永远不会消失。
编程百科
2020-08-27 13:05:15
相关推荐: 关于接口、依赖、耦合,我的一些想法 请教:关于接口的设计 在项目架构中如何进行分层才是最合理的? OO设计原则 -- OO设计的原则及设计过程的全面总结 OO的五大原则中的 LSP(zt) OO与设计模式的原则、目标 OO设计的原则及设计过程
推荐群组: Hasor框架
更多相关推荐
OO
Author:  Anders小明  
        任何一个有一定规模系统,通常会把系统做一定分解降低分析设计开发的难度,模块划分是一个比较常见的方式。
        而在模块的划分,及其分析设计的实践中,模块和模块的交互接口最为重要,通常我们认为这些接口应该通用稳定,然而如何设计每个模块对外提供的接口却是一个不易的问题。
        实践中,极有可能出现两种状况:接口维护失控或者过严而死板(而影响开发)。接口失控是因为接口的维护太过随意,因为A模块的需要就轻易在B模块中添加一个接口(方法),导致该接口(方法)非独立性(基本上只给模块A的这个功能点使用),或者是接口的控制过严,导致或者工作效率不高,或者接口的易用性不好。
原因在于:接口是两个模块间的耦合,而发生的种种问题在于模块耦合太过紧密;同时实践中,把模块对外提供的接口,与模块需要实现的外部模块的接口混为一谈。 
        根据指导原则:为了降低耦合只有在中间加一层。一种可行的实践是:不轻易为模块设计对外提供的接口(方法),除非是通过重构得来的;模块对外提供两种类:一个是需要外部模块实现的接口(接口设计从本模块需要出发,当然每个接口尽管是为某个功能点服务,但也要注意其在模块内通用性),另一个是其它模块要求本模块实现的接口的实现类。 
        即:A模块拥有一些需要B模块实现的接口(A模块对B模块的要求),而B模块中也有要求A模块实现的接口,因而A有这些接口的实现类。
        这种实践方式的好处在于:模块的接口就多了一层隔离降低了耦合,把接口的通用性和接口的适应性分离,又明确了模块的边界,使得接口在日后的优化和调整有了缓冲。
编程百科
2020-08-27 13:04:58
Readonly 写道
拍砖的来了,如果偶还想assert一把MyException的一些自定义属性呢?
catch(MyException e){
assertEquals("blahblah", e.getFoo());
assertEquals("expected error message", e.getMessage());
}
怎么用ajoo自制的裹脚布? 话说,如果你要保证一个List不被修改,简单,直接用unmodifiableList(); 而要是需要保证某个foo方法的某个bar参数不等于1,不要用unmodifiableList这个裹脚布,直接自己实现一个decorator拉倒。 如果你要assert两个对象相等,用assertEquals;如果要assert两个对象根据某个Comparator相等,不要用这个裹脚布,用assertTrue,然后自己搞错误信息。
编程百科
2020-08-27 13:04:51
相关推荐: java并发编程-Executor框架 Java并发--任务执行. 转:java线程池 常用方法 Javase JavaSE学习笔记 Java线程怎样映射到操作系统线程 JAVASE--知识总结 java工程师面试题
推荐群组: 高级语言虚拟机
更多相关推荐
OO 最近做银行业务,需要把WebSphere MQ通讯转移到Socket通讯平台,用到了java.util.concurrent包下的ExecutorService接口提供的线程池.通过Executors.newFixedThreadPool(int nThreads) 静态方法得到,但是不知道它具体的运行机制与效率如何,望大家指点.
编程百科
2020-08-27 13:04:42
相关推荐: 详解spring事务属性 关于多线程的一些问题 面向对象之弊,面向过程之优 关于service层、dao层,以及O/R Mapping之间的思考 DAO+service分层理解(论坛总结) Service Object 整理和小结 DAO层_Service层_Controller层、View层介绍 OO实现ALV TABLE 八:ALV的布局功能
推荐群组: DI
更多相关推荐
OO 以前做软件都是随便写几个Service,纯粹为了Service而Service,当某天突然发现我的两个Service竟然需要互相访问,于是乎开始考虑如何设计Service,特别是Service之间的依赖关系如何设计的问题,因此偶认为软件Service层的设计应该重点放在两个方面:一是Service 功能划分的设计;二是Service 与 Service 之间的依赖关系的设计。 其中,Service与Service之间依赖关系的设计又分如下几种: 一是继承关系依赖 public class ObjectA extends ObjectB{ } 二是属性关系依赖 public class OjbectA{ private ObjectB b; public void setB(ObjectB b){ this.b = b; } public void method(){ b.abc(); } } 三是参数方法参数关系依赖 public class ObjectA{ public void method(ObjectB b){ b.abc(); } } 大家对Spring已经很熟悉了,相信前两种大家用的很多,第三种在开源框架用的比较多.
编程百科
2020-08-27 13:04:33
相关推荐: Web AOP? 求求你们,千万别再说自己是REST了 Rails的怪异作法 -- 关于config 我的JS OO如是观 oo编程 OO方法 C语言OO编程 Java软件开发中几种认识误区
推荐群组: D语言
更多相关推荐
OO 问题是这样的。一个MyService类里面,有一个MyResponse runService()函数。这个runService函数会调用一个web service来得到MyResponse对象。这个MyResponse对象在runService()函数中被缓存,然后返回。 现在的目标是,在runService返回以前,先把MyResponse clone一下,然后如果MyResponse.getCensus().getSalary()返回的带有几毛几分的零钱,就把这个salary truncate成整数。 需求不难,一个直观的解决方案是:
java 代码
  MyResponse runService() {     MyResponse response = call web service;     cache response;     response = (MyResponse)CloneUtil.cloneSerializable(response);     SalaryTruncationUtil.truncateSalaryIfNecessary(response);      return  response;   }   CloneUtil是现成的。只要写一个SalaryTruncationUtil类就算完工了。 可是问题出现在,这个系统存在着一个ResponseManipulator接口(在另外一个package里面)。 ResponseManipulator的接口如下:
java 代码
  public   interface  ResponseManipulator {     MyResponse manipulate(MyResponse response);   }   从签名上,可以看出一个ResponseManipulator负责操作一个MyResponse,然后返回一个新的(或者原来的MyResponse)对象。 同时,另外还现存一个ChainResponseManipulator类,它负责把若干个ResponseManipulator对象串起来:
java 代码
  public   class  ChainResponseManipulator  implements  ResponseManipulator {      private  ResponseManipulator[] manipulators;      public  MyResponse manipulate(MyResponse response){        for (ResponseManipulator manipulator : manipulators) {         response = manipulator.manipulate(response);       }     }   }   以及一个已经写好的DeepCloneManipulator类来负责对一个Response对象的clone。于是,我的pair决定这样写:
java 代码
  MyResponse runService() {     MyResponse response = call web service;     cache response;     response = getManipulator().manipulate(response);   }   ResponseMinipulator getManipulator() {        return   new  ChainResponseManipulator( new  ResponseManipulator[]{          new  DeepCloneManipulator(),          new  TruncateSalaryManipulator();       });   }   当然,还要实现一个TruncateSalaryManipulator。实现起来非常简单,就不写了。 我反对这个设计。虽然两者在代码量上不相伯仲,但是我认为这个设计无谓地增加复杂性,有一种绕着弯子解决简单问题的感觉。一个简单的顺序操作非要用一个特殊的接口和一个特殊的ChainResponseManipulator来实现。 一般来说,用接口是为了得到多态,降低耦合。可是在pair的代码里,该有的依赖一个没少,这个接口就显得意义寥寥。 而且这样做其实增大了MyService的依赖,因为凭空多了对ResponseManipulator和ChainResponseManipulator的依赖。 另外,ChainResponseManipulator对debug也不是非常友好,你单步运行每一个manipulator只能在ChainResponseManipulator的代码中,而不是MyService的代码中。 pair的观点有三: 1。看不出我的方案比用Manipulator有什么简单性的优势。 2。ChainResponseManipulator这一套设施已经存在,又不需要从头写。而且别人都是这么干的。 3。debug可以在每个不同的Manipulator类里面设置断点。 总而言之,没有达成一致意见。因为是pair主导键盘,所以最终pair的方案获胜。 今天在想怎么说服pair的时候,想了这么一个例子: 假设已经有一个StringAppender类:
java 代码
  class  StringAppender {      private  Object[] objects;      public  String toString(){       StringBuffer buf =  new  StringBuffer();        for (Object obj : objects) {         buf.append(obj);       }        return  buf.toString();     }   }   那么在面对把两个对象连接成一个对象的时候,我们是:obj1.toString()+obj2.toString()呢?还是:new StringAppender(new Object[]{obj1, obj2}).toString()呢?是不是已经有了的设施,为了保持一致性就必须要使用呢?即使有更简便的方法? 关于这个问题,你怎么看?
编程百科
2020-08-27 13:04:03
TestBookStore 的OrderQ.STATUS 和TestContant的UserQ.NAME等这是什么表示法啊JDK5.0无法编译通过,只能使用JDK6.0?
编程百科
2020-08-27 13:03:47
lgx522 写道
这篇文章发在JavaEye,摆明了是讨骂。愿此文能引来大家的一阵口水,共同探讨OOP与过程式编程的是是非非。
笔者转向Java Web编程前从事的是传统的Delphi C/S编程。转过来后就觉得J2EE分层太多,每到更改,每层几乎都要涉及,相当麻烦且易出错。在Java Web之余,又涉及了一些ASP、PHP之类的东西,才发觉Web编程也可如此简单,不禁对J2EE产生了很大的疑问:本来可以简单实现的东西,何必搞得那么复杂?
近两年OO水平上涨后才逐渐领会到其中玄机。OO与分层最重要的好处其实业务逻辑及类库的复用!OOP将数据存储、业务逻辑、表示层分离,就可以适应多种数据存储方式与表示层技术的变化。比方说,你就可以选用多种数据库、XML甚至文本!也可以选择WEB、Rich Client以至于手机、PDA。当存储层与表示层变化时,业务层几乎可以保持不变。原因其实也简单:传统的过程式编程是把他们在一块写,而OO则是分开来写。而天下事总是利弊相当,OOP的高适应性也就必然为其带来高度的复杂与费时,甚至修改时的麻烦。综上所述,OOP其实是便于扩展而不适于修改。原因简单之极,写在一起的改起来只需改一处,分开写的改起来则要改每一处。
这一点上顺便提一下Sun的经典J2EE。经典J2EE其实是把OOP做到了极致,除却上述坚决的三层分离外,EJB更把数据存储层的事务与业务层的分布式特性加入其中。这对大型系统而言绝对是极大的福音。而中小型系统基本上是不需要事务与分布式的,这样一搞就让J2EE非常复杂。于是Sun的经典J2EE遭到了绝大多数中小系统应用开发者一致的口诛笔伐。可到了真正的企业级领域,J2EE的地位却难以撼动。这充分说明了OOP的极致是适应性与复杂性的综合体。这好比生物界中的生物,适应性越高的生物(如人类)必然越复杂。
是否采用OOP,OOP用到什么程度,要因软件产品的定位而论。如果考虑的是扩展性,业务逻辑相对固定,而存储层、表示层变化多的情况下,OOP当是不二之选;而存储层、表示层相对固定,业务逻辑变化大的情况下,则应选用过程式编程。如果二者变化程度均衡,则可采用MS骑墙式的方法。笔者这番大胆言论其实并非由理论推导而来,而缘于对当今软件界的实际观察。真正的企业级开发无疑J2EE是主导,这符合第一种情况;而变化迅速的互联网界则以PHP为王,小型桌面系统至今仍以VB、PB、Delphi为佳,这符合第二种情况;而中型系统,.NET发展得最好,这正是第三种情况。
所以我们搞软件的诸位同道,居于不同的阵营中,不免终日比短较长,其实是没有必要的。各种语言都有他的优势与适用范围。做好产品、服务的市场定位与技术定位,扬长避短才是明智之举。 Sun推的EJB2.0根本就不OO,Sun当年推EJB有当时的时代背景,相对比标准混乱,编程异常复杂的Corba来说,EJB是一个巨大的跨系统调用方面的进步。 Java也不是OO的的代表性语言。事实上Java在OO方面根本就和Ruby/smalltalk差的很遥远。真正把OO推到极至的是smalltalk这样的语言,至于Java,对不起,在OO领域,根本连提鞋都不配。不妨ruby,看看真正的OO是如何做到既强大又简单的。
编程百科
2020-08-27 13:03:04
相关推荐: JBossRulesUserGuide 主要部分的翻译 Drools:语言表达式:改进的数据获取方法 Drools高级技巧:Accumulate函数 Drools 3.0到4.0迁移工具 Drools4.0官方使用手册中文 Java 老矣,尚能饭否?
推荐群组: Drools
更多相关推荐
JBoss
Drools 3.0到4.0迁移工具 (Edson Tirelli)
作者:Edson Tirelli
在我们构建Drools4.0时,为了加入所有的特性,我们不得不进行一些破坏向后兼容性的API和语法变更。
为了最小化从3.0迁移到4.0的工作量,我们将提供一个工具来帮助进行规则文件的迁移。它将进行基本的自动变换,但是我们只是期望能对一切要手工调整的用户提供一些帮助。
我刚刚完成了应用的少量代码,你可以通过下面链接访问:
http://anonsvn.labs.jboss.com/labs/jbossrules/trunk/experimental/drools-update/
现在,只实现了对assert, assertLogical 和 modify 的调用替换成, insertLogical and update。但是我们期望能够逐步实现覆盖其它语法。例如解决绑定类型以知道何时可以删除对原型数据的解包方法调用,如intValue等等。
欢迎任何希望对该任务提供帮助的人,只要通过IRC或邮件列表加入我们。
 
编程百科
2020-08-27 13:02:30
相关推荐: 《Velocity1.4 java开发指南》中文版 Drools3.1M1 发布标注 Lucene-2.0学习文档(1) Drools高级技巧:Accumulate函数 Drools 5.1.1(一) Drools代替复杂业务 Drools 规则引擎----向领域驱动进步(三) Drools 规则文件语法概述
推荐群组: JBPM @net
更多相关推荐
JBoss  
Accumulate 函数 (Edson Tirelli)
作者: Mark Proctor
当我们接近完成发布版,一切事情趋于它最终的形状。这个星期是将 Accumulate 条件元素完成的日子。对于不了解它的人, Accumulate 是 Drools4.0 中非常强大的一个条件元素。它允许你对数据集进行操作
通常它的语法如下:
ResultPattern ( fieldconstraint* )
from accumulate ( SourcePattern ( fieldconstraint* )
                   init ( code )
                  action ( code )
                  reverse ( code )
                  result ( code ) )
 
基本上, accumulate 所 做的是执行 init 代码块一次 ,然后迭代所有匹配 SourcePattern 的 fact ,对每一个 fact 执行 action 代码块并且最后执行 result 代码块一次。所获得的结果按照 ResultPattern 匹配,结果为真时满足条件。 Reverse 代码块是可选的,它的功能是当之前被 SourcePattern 匹配 的 fact 被删除或修改时改善执行性能。

好了,没有什么比一个例子更能说明情况了
规则:对包括至少价值 100 元以上的玩具的订单给以 10% 的折扣
rule "Discount for orders that include US$100,00 of toys"
when
$o : Order()
$toysTotal : Number( doubleValue > 100 )
  from accumulate ( OrderItem( order == $o, type == "toy", $value : value ),
                     init ( double total = 0; ),
                   action ( total += $value; ),
                   reverse ( total -= $value; ), // 如果有订单被删除,则从总计中除去金额
                   result ( new Double( total ) ) )
then
  $o.setDiscountPercentage( 10 );
end
你可以从上面的例子看到, accumulate 是非常灵活和强大的。每一个代码块可以是 Java 或 MVEL 代码块,可以在这里进行任何操作。

但是,有些人会说:“ 好, accumulate 是很灵活和强大,但是我不想为上面这样的常见操作不断编写代码。” 因此,这是我们这个星期正在进行的工作。我们非常希望你可以对常见的情况以更简单的方式来使用 accumulate 。因此 Accumulate 功能被继续改进。

你现在可以使用预定义的函数来简化 accumulate 常见情况的使用。例如,上面的规则使用 accumulate 执行一个值累加。同样的规则可以写成这样:

rule "Discount for orders that include US$100,00 of toys"
when
$o : Order()
$toysTotal : Number( doubleValue > 100 )
             f rom accumulate ( OrderItem( order == $o, type == "toy", $value : value ),
                              sum ( $value ) )
then
$o.setDiscountPercentage( 10 );
end
现在更简单了。 如果你希望建立一个可以告诉你为每一个部门提升 X% 的工资会为你带来多大的花费的规则。如下示例:
rule "Total raise"
when
$dpt : Department( $raise : raise )
$total : Number()
         from accumulate ( Employee( dept == $dpt, $salary : salary ),
                            sum ( $salary * $raise ) )
then
$dpt.setSalaryIncreaseValue( $total );
end
这样,你可以将使用任何表达式作为参数传递到 accumulate 功能。我们为你增加了大多数最常用的功能如: sum , average , count , min , max 等等。
 
但是你会说: “我喜欢这些函数,但是我希望规则能够使用我自己定义的函数。我可以实现这些函数并提供给他们,这样他们就不用重复的编写同样代码了吗?”
我们的答案是 : " 当然可以 !" ;)
我们使用可以达到的最简单的方式来使 Accumulate 函数 具有可插入性,因此你可以很简单的提供新的函数来给你的用户使用。例如,假设你有一个非常复杂的计算,需要获得一个指定股票交易操作的费用。你的用户正在你的专家系统中编写规则,使得它可以建议哪一种操作方式是更有利可图的,并且他们已经有一些规则需要计算这样的股票交易操作的费用。
要开发一个新的 accumulate 函数,你唯一要做的事情是开发一个实现了 AccumulateFunction 接口的 java 类。这个接口有一个方法与 Accumulate 的每一个操作( init, action, reverse and result )进行交互。可以用来实现类似计算平均数这样的函数(代码附后)。
 
最后,要在系统里使用你的自定义函数,你可以调用一个 API (addAccumulateFunction()) 或者定义一个属性。这个属性可以在配制文件中或者作为系统属性定义,例如:

drools.accumulate.function.average = org.drools.base.accumulators.AverageAccumulateFunction
就这么简单,希望你能感到满意。
Happy Drooling!
 
 
附录:平均数函数实现代码 /* * Copyright 2007 JBoss Inc *   * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at *   *       http://www.apache.org/licenses/LICENSE-2.0 *   * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * Created on Jun 21, 2007 */ package org.drools.base.accumulators;     /** * An implementation of an accumulator capable of calculating average values *   * @author etirelli * */ public class AverageAccumulateFunction implements AccumulateFunction {       protected static class AverageData {         public int     count = 0;         public double total = 0;     }       /* (non-Javadoc)      * @see org.drools.base.accumulators.AccumulateFunction#createContext()      */     public Object createContext() {         return new AverageData();     }       /* (non-Javadoc)      * @see org.drools.base.accumulators.AccumulateFunction#init(java.lang.Object)      */     public void init(Object context) throws Exception {         AverageData data = (AverageData) context;         data.count = 0;         data.total = 0;     }       /* (non-Javadoc)      * @see org.drools.base.accumulators.AccumulateFunction#accumulate(java.lang.Object, java.lang.Object)      */     public void accumulate(Object context,                            Object value) {         AverageData data = (AverageData) context;         data.count++;         data.total += ((Number) value).doubleValue();     }       /* (non-Javadoc)      * @see org.drools.base.accumulators.AccumulateFunction#reverse(java.lang.Object, java.lang.Object)      */     public void reverse(Object context,                         Object value) throws Exception {         AverageData data = (AverageData) context;         data.count--;         data.total -= ((Number) value).doubleValue();     }       /* (non-Javadoc)      * @see org.drools.base.accumulators.AccumulateFunction#getResult(java.lang.Object)      */     public Object getResult(Object context) throws Exception {         AverageData data = (AverageData) context;         return new Double( data.count == 0 ? 0 : data.total / data.count );     }       /* (non-Javadoc)      * @see org.drools.base.accumulators.AccumulateFunction#supportsReverse()      */     public boolean supportsReverse() {         return true;     }   }
 
编程百科
2020-08-27 13:02:24
相关推荐: drools 4.0M3发布标注 为何在Drools中使用MVEL Prototype.AjaxRequest的调用堆栈重写问题 JBoss Rules 学习(七): Drools规则语言详解(下) Drools 5.1.1(一) jboss drools_JBoss Drools –入门 开源业务规则引擎JBoss Drools入门介绍 Demo示例 JBoss Drools –入门
推荐群组: OFBiz
更多相关推荐
JBoss  
语言表达式:改进的数据获取方法
作者: Edson Tirelli
大多数人都知道, Drools4.0 的一个主要目标是增强表达式的能力,并简化规则语言。
对于大家所期待的常用语言表达方式,我们高兴得说,在 4.0MR3 版本中这个特性已经完成了,新的改变允许嵌套访问,映射和数组的标准访问语法,以及复杂的表达式评估。换句话说,用户现在可以编写约束如下:
rule "Complex expressions"
when
$country : Contry( name == "Brazil" )
Person( address['business'].phone[0].contryCode == $country.phone.countryCode )
then
// do something
end
在上面的规则中‘ address ’是一个对象映射,关键字 'business' 与 一个 Address 对象 (POJO) 相连,它包含了一个 Phone 对象 (POJO) 数组,我们要访问的是这个数组的第一个 Phone 对象的 CountryCode 属性 。

这意味着用户不再需要担心扁平对象模型的建模了吗?
Does that mean that users are not required to worry about modeling flatter object models anymore?
实际情况不是这样。为了能够发挥规则引擎的最大能力,我们仍然建议用户必须使用一个关系对象模型。虽然我们提供上面的功能以允许在关系模型无法与整个应用设计相适应时,有另一种好的选择。
要特别理解的是,对于上面的表达式来说被转换到 inline-evals()** , 因此他们必须要保持数据不变以避免不可预期的行为。同样也要注意的是它不能 shadow (投影)整个对象图,同样当 fact 在 working memory 期间,需要保证约束属性不被改变。
** 注 : 在 4.0 中 , 我们调整了一个术语,在 3.0.X 中被称为 predicate (断言) 的术语,现在在 4.0 中被称为 " inline-eval (内嵌求值) " 。改变的原因是,我们不久将支持另一个被称为 predicate (断言) 的 解释。

Drools 开发组
编程百科
2020-08-27 13:02:13
public static void testCase(int x) {switch (x) { case 1: System.out.println("is 1"); break; case 2: case 3: case 4: System.out.println("is 2 3 4"); break; default: x = x>4?5:-1; case 5: System.out.println("big 4"); break; case -1: System.out.println("less 1"); } } Head First设计模式■ 群共同努力
编程百科
2020-08-27 13:02:04
风之巽 写道
您好!请问关于符号表的建立有没有相关的例子啊?
最近看论文关于增量编译的,提到了复用子树的概念,就是将上一次分析得到的树中的可复用的结点直接移进解析栈,而不用一个个终结符来分析。。。
可是javacc中关于jjtree我还是很不了解,想要建立解析表之类的也不知道怎么样开始,能帮忙指点下吗??谢谢您! javacc的例子中有一个interpreter,解释一个叫SPL的小语言(也就是我这篇文章给出的例子的来源),其中用到了符号表,其实就是一个hashtable,当遇到终结符后,将键值对插入其中。其实就是一个全局的表结构,可以从中根据名称取出值来。另,不用这么客气。
编程百科
2020-08-27 13:01:50
具体问题具体分析,要考虑方法的可访问性等问题,如果真这么多方法的话,虽然第一种不可能,所有调用都在一个方法里可能性也不大,要考虑具体职责该归哪个方法,也要考虑这个方法是否有资格访问另一个方法,一些都为了封装性考虑,能不暴露的就不暴露
编程百科
2020-08-27 13:01:26
相关推荐: 关于String字面常量的问题 JAVA6可以使用字符串累加 深入了解Java ClassLoader、Bytecode 、ASM、cglib override, overload, covariance 为什么说Java匿名内部类是残缺的闭包 Java字节码揭秘——第一部分 收藏 JAVA基础---synchronize底层证明 字节码及ASM使用
推荐群组: 高级语言虚拟机
更多相关推荐
OO javap是jdk自带的一个工具,可以反编译,也可以查看java编译器生成的字节码,是分析代码的一个好工具。javap - Java class文件分解器 分解class文件 摘要: javap [ options ] class. . . 描述: javap命令分解一个class文件,它根据options来决定到底输出什么。如果没有使用options,那么javap将会输出包,类里的protected和public域以及类里的所有方法。javap将会把它们输出在标准输出上。来看这个例子,先编译下面这个类。 import java.awt.*; import java.applet.*; public class DocFooter extends Applet { String date; String email; public void init() { resize(500,100); date = getParameter("LAST_UPDATED"); email = getParameter("EMAIL"); } public void paint(Graphics g) { g.drawString(date + " by ",100, 15); g.drawString(email,290,15); } } 在命令行上键入javap DocFooter后,输出结果如下 Compiled from DocFooter.java public class DocFooter extends java.applet.Applet { java.lang.String date; java.lang.String email; public DocFooter(); public void init(); public void paint(java.awt.Graphics); } 如果加入了-c,即javap -c DocFooter,那么输出结果如下 Compiled from DocFooter.java public class DocFooter extends java.applet.Applet { java.lang.String date; java.lang.String email; public DocFooter(); public void init(); public void paint(java.awt.Graphics); } Method DocFooter() 0 aload_0 1 invokespecial #1 4 return Method void init() 0 aload_0 1 sipush 500 4 bipush 100 6 invokevirtual #2 9 aload_0 10 aload_0 11 ldc #3 13 invokevirtual #4 16 putfield #5 19 aload_0 20 aload_0 21 ldc #6 23 invokevirtual #4 26 putfield #7 29 return Method void paint(java.awt.Graphics) 0 aload_1 1 new #8 4 dup 5 invokespecial #9 8 aload_0 9 getfield #5 12 invokevirtual #10 15 ldc #11 17 invokevirtual #10 20 invokevirtual #12 23 bipush 100 25 bipush 15 27 invokevirtual #13 30 aload_1 31 aload_0 32 getfield #7 35 sipush 290 38 bipush 15 40 invokevirtual #13 43 return 当然,如果想分析这个文件,可以讲输出结果输出到一个文件里。可以这样写 javap -c DocFooter > F://test.txt 这样就会输出到F盘的test.txt文件中了。 选项: -help 不说了 -l 输出行和变量的表 -public 只输出public方法和域 -protected 只输出public和protected类和成员 -package 只输出包,public和protected类和成员,这是默认的 -private 输出所有类和成员 -s 输出内部类型签名 -c 输出分解后的代码,例如,类中每一个方法内,包含java字节码的指令, -verbose 输出栈大小,方法参数的个数 。。。 http://java.sun.com/j2se/1.5.0/docs/tooldocs/windows/javap.html
编程百科
2020-08-27 13:01:17
相关推荐: Scala2.8探秘之for表达式 python娱乐下 Project Euler解题汇总 001 ~ 012 Scala2.8尝鲜:使用Numeric让基本类型范型化 大数据—Scala Scala的面向对象(OO)编程入门 Failed to execute goal org.scala-tools:maven-scala-plugin:2.15.2:compile(default) on Project DataFus
推荐群组: Scala圈子
更多相关推荐
OO 处理基本类型是Java中最烦人的一个地方了,很多时候我们必须为每一种基本类型各写一份大同小异的代码。这造成了严重的代码冗余,对代码的维护也带来负担。虽然在Java5中引入了范型,但对基本类型的操作并没有带来任何便利。考虑一个简单的例子: 设计一个方法sum,用于计算两个数的和,并且返回值的类型与参数类型一致。 在Java中,我们只能通过重载sum方法,为每一种基本类型写一个sum方法: public int sum(int a,int b){ return a+b} public long sum(long a,long b){return a+b} public float sum(float a,float b){return a+b} public double sum(double a,double b){return a+b} ... 显然比较理想的方式应该是我们用一个类似于 T sum(T a,T b){ return a+b } 的方法一劳永逸的适用于所有的问题。 在Scala2.8中,新增了一个trait Numeric以及与之相关的类,使用这个类我们就可以得到类似的效果: Welcome to Scala version 2.8.0.r18591-b20090828020156 (Java HotSpot(TM) Client VM, Java 1.6.0_14). Type in expressions to have them evaluated. Type :help for more information. scala> def sum[T](a :T,b :T)(implicit m:Numeric[T]) :T ={ | import m._ | a + b | } sum: [T](a: T,b: T)(implicit m: Numeric[T])T scala> sum(1,2) res0: Int = 3 scala> sum(1.0,2.0) res1: Double = 3.0 注意方法sum的参数中包含一个 隐式参数 implicit m:Numeric[T],这个参数在调用的时候无需显式指定,编译器会自动找到相应的值做参数。 类似的,我们可以定义一个通用的max方法: scala> def max[T](a :T,b :T)(implicit m:Numeric[T]) :T = { | import m._ | if(a >= b) a else b | } max: [T](a: T,b: T)(implicit m: Numeric[T])T scala> max(1,2) res2: Int = 2 scala> max(1.0,2.0) res3: Double = 2.0 scala> 而且这个Numeric只是往以前的核心库中添加了一组API,并没有使用任何Scala2.8中新的语法;在Scala2.7.x中就能实现它。
编程百科
2020-08-27 13:00:49
相关推荐: 请教:关于接口的设计 接口还是继承 没有方法的接口 什么时候使用接口?什么时候使用抽象?选择Java接口还是抽象类 (转) 设计RPC接口时,你有考虑过这些吗? 我们为什么要使用空接口 Java 接口大全、面向接口编程、什么时候使用接口。选择Java接口还是抽象类 OO真经——关于面向对象的哲学体系及科学体系的探讨
推荐群组: D语言
更多相关推荐
OO 接口存在的意义已经有几个帖子讨论过了,我也有自己的一些想法,希望大家指正。接口是面向对象中的重要概念,它是为了实现面向对象的思想而存在的,具体来说,我个人认为面向对象主要有封装,继承和多态。接口主要是用来实现继承和多态的,尤其是多态。如果设计一个系统时没有必要采用继承和多态的东西,我个人认为是没有必要用接口的,反之,则有这个必要。 1.大多人开发用的系统一个接口只有一个实现,在这种情况下,继承和多态没有运用,我个人认为接口在这种情况下没有必要使用,这个接口的作用完全可以用实现类来体现。在接口的实现需要改变的时候,完全可以直接修改这个类的内部实现,只要不改变这个类的公有方法。 2.接口在框架,标准的设计中是非常有用的。 比如在J2EE中,接口被广泛使用,那么为什么呢?重要原因是因为这个接口有很多种实现,不同的厂商有不同的实现,也就是说有继承和多态。 3可能有些接口刚开始只有一个实现类,后期可能有几个实现类。 在这种情况下,完全可以刚开始不引入接口,后期如果有必要再引入,只要勇于重构自己的代码,接口完全可以自动浮现出来。 总的看法是 接口只有在必需的时候再运用。
编程百科
2020-08-27 13:00:33
相关推荐: 线程池示例代码,请大家多指教 ibatis2.3源码之数据源&连接池浅析 commons-pool-1.6.0对象池技术 一篇关于apache commons类库的详解 21天通关Python(仅视频课) 学习MySQL这一篇就够了
推荐群组: 高级语言虚拟机
更多相关推荐
OO 最近也是在看Apache Commons Pool的源码.所以总会想一些问题. 1,你们项目中哪些对象放入到pool中了???? 是"轻对象"还是"重对象"呢? 2,Commons Pool的设计基本思想是 只存储不活动对象 ,不存储活动对象(这应该符合pool的初衷).但最近我是看到2个项目中的pool的写法都是既存储了active对象,也存储了sleeping对象,为放入pool的对象都搞了个status..这样在需要从pool中取对象的时候,就去while遍历池中所有对象,当status=0(1,活动;0,不活动)时候,return obj; 举例: while(probe!=null){ if(probe.status==1) return probe; else probe = probe.next; } 3,pool的意义上为了节省资源,不用频繁的去Constuction对象.. SoftReference 应该是个很好的想法吧(我觉得).它的好处是内存不足的时候,GC无条件的去回收pool中空闲的对象..Commons Pool的部分分析可以看: http://c-j.iteye.com/admin/blogs/378085 代码还没看完,有时间会继续学习的~ 4,对于有一定"状态"的对象,在borrow & return的时候,有没有考虑过"恢复"对象?比如恢复对象某些属性值为0等操作...
编程百科
2020-08-27 13:00:15
相关推荐: Customize your log message AJAX POST时的中文乱码问题和解决 URL传递中文参数中乱码问题的解决方案 在Log4j中实现日志的Email自动发送 7.Java中的异常、断言、日志【草稿下,Log4j专题】 log4j 总结和提高 ABAP OO EMAIL
推荐群组: 垂直搜索
更多相关推荐
OO
HTML格式:
package com.just.zb.email.log; import java.text.SimpleDateFormat; import java.util.Date; import org.apache.log4j.Layout; import org.apache.log4j.Level; import org.apache.log4j.helpers.Transform; import org.apache.log4j.spi.LocationInfo; import org.apache.log4j.spi.LoggingEvent; public class EmailHTMLLayout extends Layout { protected int BUF_SIZE = 256; protected int MAX_CAPACITY = 1024; static String TRACE_PREFIX = "
"; private StringBuffer sbuf; /** @deprecated */ public static final String LOCATION_INFO_OPTION = "LocationInfo"; public static final String TITLE_OPTION = "Title"; boolean locationInfo; String title; public EmailHTMLLayout() { this.BUF_SIZE = 256; this.MAX_CAPACITY = 1024; this.sbuf = new StringBuffer(256); this.locationInfo = false; this.title = "Log4J Log Messages"; } public void setLocationInfo(boolean flag) { this.locationInfo = flag; } public boolean getLocationInfo() { return this.locationInfo; } public void setTitle(String title) { this.title = title; } public String getTitle() { return this.title; } public String getContentType() { return "text/html;charset=GBK"; } public void activateOptions() { } private SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); public String format(LoggingEvent event) { if (this.sbuf.capacity() > 1024) this.sbuf = new StringBuffer(256); else { this.sbuf.setLength(0); } this.sbuf.append(Layout.LINE_SEP + "" + Layout.LINE_SEP); this.sbuf.append(""); this.sbuf.append( format.format(new Date(event.timeStamp))); this.sbuf.append("" + Layout.LINE_SEP); String escapedThread = Transform.escapeTags(event.getThreadName()); this.sbuf.append(""); this.sbuf.append(escapedThread); this.sbuf.append("" + Layout.LINE_SEP); this.sbuf.append(""); if (event.getLevel().equals(Level.INFO)) { this.sbuf.append(""); this.sbuf.append(Transform.escapeTags(String.valueOf(event.getLevel()))); this.sbuf.append(""); } else if (event.getLevel().isGreaterOrEqual(Level.WARN)) { this.sbuf.append(""); this.sbuf.append(Transform.escapeTags(String.valueOf(event.getLevel()))); this.sbuf.append(""); } else { this.sbuf.append(Transform.escapeTags(String.valueOf(event.getLevel()))); } this.sbuf.append("" + Layout.LINE_SEP); StringBuffer buff= new StringBuffer(""); buff.append(event.getLocationInformation().getClassName() ); buff.append("."); buff.append(event.getLocationInformation().getMethodName() ); buff.append("("); buff.append(event.getLocationInformation().getFileName() ); buff.append(":"); buff.append(event.getLocationInformation().getLineNumber()); buff.append(")"); // System.out.println(buff); String lineLoger = Transform.escapeTags(buff.toString()); this.sbuf.append(""); this.sbuf.append(lineLoger); this.sbuf.append("" + Layout.LINE_SEP); if (this.locationInfo) { LocationInfo locInfo = event.getLocationInformation(); this.sbuf.append(""); this.sbuf.append(Transform.escapeTags(locInfo.getFileName())); this.sbuf.append(':'); this.sbuf.append(locInfo.getLineNumber()); this.sbuf.append("" + Layout.LINE_SEP); } this.sbuf.append(""); this.sbuf.append(Transform.escapeTags(event.getRenderedMessage())); this.sbuf.append("" + Layout.LINE_SEP); this.sbuf.append("" + Layout.LINE_SEP); if (event.getNDC() != null) { this.sbuf.append(""); this.sbuf.append("NDC: " + Transform.escapeTags(event.getNDC())); this.sbuf.append("" + Layout.LINE_SEP); } String[] s = event.getThrowableStrRep(); if (s != null) { this.sbuf.append(""); appendThrowableAsHTML(s, this.sbuf); this.sbuf.append("" + Layout.LINE_SEP); } return this.sbuf.toString(); } void appendThrowableAsHTML(String[] s, StringBuffer sbuf) { if (s != null) { int len = s.length; if (len == 0) return; sbuf.append(Transform.escapeTags(s[0])); sbuf.append(Layout.LINE_SEP); for (int i = 1; i < len; ++i) { sbuf.append(TRACE_PREFIX); sbuf.append(Transform.escapeTags(s[i])); sbuf.append(Layout.LINE_SEP); } } } public String getHeader() { StringBuffer sbuf = new StringBuffer(); sbuf.append("" + Layout.LINE_SEP); sbuf.append("" + Layout.LINE_SEP); sbuf.append("" + Layout.LINE_SEP); sbuf.append("" + this.title + "" + Layout.LINE_SEP); sbuf.append("" + Layout.LINE_SEP); sbuf.append("" + Layout.LINE_SEP); sbuf.append("" + Layout.LINE_SEP); sbuf.append("
" + Layout.LINE_SEP); sbuf.append("Log session start time " + new Date() + "
" + Layout.LINE_SEP); sbuf.append("
" + Layout.LINE_SEP); sbuf.append("" + Layout.LINE_SEP); sbuf.append("" + Layout.LINE_SEP); sbuf.append("" + Layout.LINE_SEP); sbuf.append("" + Layout.LINE_SEP); sbuf.append("" + Layout.LINE_SEP); sbuf.append("" + Layout.LINE_SEP); if (this.locationInfo) { sbuf.append("" + Layout.LINE_SEP); } sbuf.append("" + Layout.LINE_SEP); sbuf.append("" + Layout.LINE_SEP); return sbuf.toString(); } public String getFooter() { StringBuffer sbuf = new StringBuffer(); sbuf.append("
TimeThreadLevelCategoryFile:LineMessage
" + Layout.LINE_SEP); sbuf.append("
" + Layout.LINE_SEP); sbuf.append(""); return sbuf.toString(); } public boolean ignoresThrowable() { return false; } }  
发送模式
package com.just.zb.email.log; import org.apache.log4j.net.SMTPAppender; import org.apache.log4j.spi.LoggingEvent; public class EmailSMTPAppender extends SMTPAppender { public void append(LoggingEvent event) { if (!checkEntryConditions()) { return; } event.getThreadName(); event.getNDC(); event.getMDCCopy(); if (this.getLocationInfo()) { event.getLocationInformation(); } cb.add(event); if (evaluator.isTriggeringEvent(event)) { if (cb.length() > this.getBufferSize()-24) { sendBuffer(); } } } public EmailSMTPAppender() { super(); Runtime.getRuntime().addShutdownHook(new Thread() { @Override public void run() { if (cb.length() > 0) { sendBuffer(); } } }); } }  
LOG等级
package com.just.zb.email.log; import org.apache.log4j.Level; import org.apache.log4j.spi.LoggingEvent; import org.apache.log4j.spi.TriggeringEventEvaluator; public class EmailTriggeringEventEvaluator implements TriggeringEventEvaluator { public boolean isTriggeringEvent(LoggingEvent paramLoggingEvent ) { return paramLoggingEvent.getLevel().isGreaterOrEqual(Level.INFO); } }
配置文件
log4j.rootCategory=INFO,CONSOLE,FILE,EMAIL log4j.category.com.just=INFO #log4j.rootCategory=DEBUG,CONSOLE,FILE #log4j.category.com.just=DEBUG log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout log4j.appender.CONSOLE.layout.ConversionPattern=[%p]: %d{yyyy-MM-dd HH:mm:ss} %n |_[MESSAGE]: %m %n |_[SOURCE] : %l%n%n log4j.appender.FILE=org.apache.log4j.DailyRollingFileAppender log4j.appender.FILE.file=log/utils.log log4j.appender.FILE.layout=org.apache.log4j.PatternLayout log4j.appender.FILE.layout.ConversionPattern=[%p]: %d{yyyy-MM-dd HH:mm:ss} %n |_[MESSAGE]: %m %n |_[SOURCE] : %l%n%n log4j.appender.EMAIL=com.just.zb.email.log.EmailSMTPAppender log4j.appender.EMAIL.Threshold=INFO log4j.appender.EMAIL.BufferSize=1024 log4j.appender.EMAIL.To=zhongbin@just-tech.com.cn log4j.appender.EMAIL.From=www.just-tech.com.cn log4j.appender.EMAIL.Subject=SYSTEM ERROR[ TEST ] log4j.appender.MAIL.SMTPDebug=true log4j.appender.EMAIL.SMTPHost=mail.just-tech.com.cn #log4j.appender.EMAIL.SMTPUsername=youemail #log4j.appender.EMAIL.SMTPPassword=youpw log4j.appender.EMAIL.EvaluatorClass=com.just.zb.email.log.EmailTriggeringEventEvaluator log4j.appender.EMAIL.layout=com.just.zb.email.log.EmailHTMLLayout  
编程百科
2020-08-27 13:00:05
相关推荐: JAVA 需要引入闭包吗 IE下闭包引起跨页面内存泄露探讨 javascript 闭包的一个例子 再论闭包 应届生,一口气拿下微软、Hulu、Amazon、腾讯、百度、网易、美团、华为等18家公司SP Offer面经... 《Spring Boot极简教程》附录1 计算机简史
推荐群组: F#语言
更多相关推荐
OO
-- 再论闭包 --
 
 
下面这个例子 引自 《Groovy in action》 第五章。
 
一段 Java 代码, interface ResourceUser { void use(Resource resource) } resourceHandler.handle(new ResourceUser(){ public void use (Resource resource) { resource.doSomething() } });
 
下面是一段等价 Groovy 的代码。 resourceHandler.handle { resource -> resource.doSomething() }
 
惊叹吧 ?  8行JAVA代码居然被 Groovy 如此 easy 地实现了。
惊叹! 惊叹之余再来反思。。。
 
(1), 为何能如此简单(Groovy):因为它在使用闭包。
        Groovy中 行为元素(函数,过程)作为一级元素。
        像resource -> resource.doSomething() 就是一个匿名函数。
        参数 为 resource,返回值 为 resource.doSomething()。
 
(2), 为何如此复杂(Java):因为它在模拟闭包。
        函数咱没有,那就包装一下,模拟一下,但好像臃肿了点。。
 
(3), 易理解吗?
       Groovy : 资源处理器 (名词:resourceHandler) 处理 (动词:handle) 一个函数 (匿名函数:resource ->  
                      resource.doSomething())。
       模拟闭包的Java : 资源处理器 (名词:resourceHandler) 处理 (动词:handle) 一个资源用户 (名词:new
                      ResourceUser(){...})。
       资源处理器应该只处理资源啊。。。
 
(4), 重新造句 (Java)
       OO的Java : 资源处理器 (名词:resourceHandler) 处理 (动词:handle) 一个资源 (名词:resource)。
  new ResourceHandler(){ protected void handle(Resource resource){ //doSomething .... resource.doSomething(); //doSomething .... } }.handle(request);
 
 
个人观点 :
 
一种功能如果可以用FP中的闭包来实现,
那它必然也可以用OO中的对象来实现, 而且更贴近于人的思维。
有必要用对象模拟闭包吗?个人感觉没必要,
用对象模拟的闭包 来实现一种功能,感觉相比上诉两种情况更复杂。
 
 
编程百科
2020-08-27 12:59:56
相关推荐: Spring service中ThreadLocal的应用? bbossgroups 线程池使用 操作属性文件的步骤 这些年一直记不住的 Java I/O java学习小知识集锦2 java面试题集中了好几篇的搜索的 hadoop面试100道收集(带答案) Hadoop面试题 (网上收集版带答案)
推荐群组: 高级语言虚拟机
更多相关推荐
OO
利用 properties 资源文件追加写入,而不覆盖
Properties pro = new Properties();
            pro.load( new FileInputStream( "readxmllasttime.porperties" ));
            for (Enumeration e = pro.propertyNames(); e.hasMoreElements();) {
                String s = (String) e.nextElement(); // 遍历所有元素
                if (s.equals(key)) {
                    /** 如果键相同则覆盖 */
                    pro.setProperty(key, maxdate.getTime() + "" );
                } else {
/** 否则就原样写入 */
                    pro.setProperty(s, pro.getProperty(s));
                }
            }
            pro.store( new FileOutputStream( "readxmllasttime.porperties" ), " 描述信息 " );
 
 
编程百科
2020-08-27 12:59:31
相关推荐: 请教:关于接口的设计 名词探讨:需求,接口,interface 关于SRP原则的问题 OO真经——关于面向对象的哲学体系及科学体系的探讨 OO方法 接口的理解 OO
推荐群组: D语言
更多相关推荐
OO 很老的话题,却又日常的东西.回顾一下历程总是好的: -->1. 最初直接用Ojbect -->2. 随着模式的兴起,大家意识到interface的好处 -->3. 面向interface编程 -->4. 随着AOP的兴起,interface的作用减弱 -->5. class即是interface --->6. 接下来是... 回顾的原因是,用不用interface,只有三种答案:全用(参照3),全不用(参照5),部份用(不讲原因的话,就是模棱两可以的答案)。单讲答案是很易引起错的争论。 我也知道大部人对(3)有很深认识, 做AOP方面多或者用动态语言(比方用ruby)的人,对(5)的理解会深一些. 我想说的是: 从coding的角度:class是interface, 从deploy的角度,安全的角度:class不是interface. 所以现在的问题是: 没有好的tools做deploy(或者根本没人有意识这么去做), 它能自动的从class中抽出interface,做成可deploy的jar. 所以, 在安全性要求较高的大型应用中,我们不得不依旧忍受着"面向interface编程"的痛苦. 附:一个小的story: Developer: 我想直接做一个送外买的Seller PM: 不行,在这之前,你必需先做一个叫ISeller的interface Developer: 为什么这么麻烦啊,不做不行? PM: 真正的Seller不是你想的那么简单,以后这个Seller说不定会换成会跳舞的Seller,也有可能会换成会唱歌的Seller.直接给出实现不利于以后的扩展。 (面向interface) Developer: XP都说了,不要因为以后的可能,增加现在的负担。再说,就算以后要改成会跳舞的Seller,AOP可以搞定它。 PM: 恩,你说的有点道理。。。不过,现在的世道不安全啊,路上的黑客太多。在路上跑的还是用一个ISeller的interface安全些。 Developer:有没有办法做个自动的interface啊? PM:没找到这样的tools。 Developer:好吧。。。那我多做一个interface好了。 自此,500个object带着500个interface,不安全的地方,派interface上去,中间加防火墙,interface与object说话用暗语(比方叫啥soap的来着), 终于世界终于安全了。
编程百科
2020-08-27 12:59:22
zhangyou1010 写道
1.继承中的:
"(4) 一个对象变量可以引用多种实际类型(如Employee对象引用Employee和Manager对象),在运行时它能够自动地选择调用的是适当的方法。Java不支持多继承。可以将一个子类对象的引用赋值给一个超类变量,但是不能用这个引用调用子类的方法,不能将一个超类的引用赋给子类变量。"
有问题。父类引用可以指向子类对象,这个没错,但是"但是不能用这个引用调用子类的方法" 这里恰恰说反了,只能引用子类的方法,但不能引用子类的属性。 不好意思,我看错了,我以为楼主说的是不能调用子类override的方法。
编程百科
2020-08-27 12:59:15
这个粒度没必要太细,但是为了让其他开发人员能够快速的发现错误(这也是层次化的最终目的),最好把某一个大类的Exception统一,对于infrastructure的,可以按照切面归类,比如IO异常啥的,对于business的,可以按业务功能内聚性归类,比如XXXBusinessException啥的,在这些大类之外,更重要的是异常代码和异常消息,对于抛出的异常最好赋予一个error code,这个可以进一步找到具体的错误原因了
编程百科
2020-08-27 12:59:06
相关推荐: 读《Java核心技术卷I 》Java继承<第五章> javascript mvc框架backbone.js 初探 【解惑】理解java枚举类型 java学习——品尝OO的味道(一) Java程序设计 试卷A java、八大经典书籍,你看过几本? Java IO:了解I/O模型 2017JAVA面试题附答案
推荐群组: JBPM @net
更多相关推荐
OO
1.    命令行参数
     如在控制台输入 java Message –h cruel world
     args 的参数包含如下:
     args[0]=”-g”
     args[1]=”cruel”
     args[2]=”world”
2.    面向对象
     一个对象变量并没有实际包含一个对象,而仅仅引用一个对象。 new 操作符的返回值也是一个引用。相当于 C++ 中的对象指针。 Java 中的 null 相当于 C++ 中的 NULL 。
3.    时钟类
     (1)   GregorianGalendar 类扩展了 Calendar 类。
         默认构造函数构造一个表示对象被创建时的日历对象。可以创建特定年月日的对象,如
         new GregorianCalendar(1999,Calendar.December,32) 或者
         new GregorianCalendar(1999,11,32)
         月份从 0 开始计数。也可以构造具有年月日时分秒的日历对象。
     (2)   访问日历中的年月日等,如
         int month=now.get(Calendar.MONTH);// 得到的数为月数 -1
         int weekday=now.get(Calendar.DAY_OF_WEEK);
     (3)   设置对象的状态,如:
         deadline.set(Calendar.YEAR,2001);
         deadline.set(Calendar.MONTH,Calendar.APRIL);
         deadline.set(Calendar.DAY_OF_MONTH,15);
     (4)   为给定的日历对象添加天数、星期数、月数等
         deadline.add(Calendar.MONTH,3);
     (5)   Data 与 GregorianCalendar 的变换 (Date 类无法操纵日历 )
         Data time=calendar.getTime();
         calendar.setTime(time);
     (6)   可以通过 DateFormatSymbols 类来获取别国语言的星期几的名称。
4.    java 的编译
     (1)   文件名必须和 ppublic 类的名字相匹配,在一个源文件中,只能有一个公有类,但可以有任意数目的非公有类。
     (2)   编译方法:
1)    通配符调用 java 编译器,如 java Employee*.java, 将编译所有以 Employee 开头的源文件。
         2)    javac EmployeeTest.java 由于 EmployeeTest 中使用了 Employee ,所以编译器将自动编译 Employee 类。
         3)    Employee number(“James Bond”,100,1950,1,1); 这条语句在 C++ 中能够正常运行,但在 java 中不能运行。
5.    java 程序员应该注意的几个问题
     (1)   不要编写返回引用可变对象的访问器的方法,因为在类外可以改变这个引用的状态,起不到访问器的作用。
     (2)   一个方法可以访问所属类的所有对象的私有数据。例如:
         Class Employee{
              boolean equals(Employee other){
                   return name.equals( other.name );
}
}
6.    (1)   final :在创建变量之后,指能够为之赋值一次,此后再也不能改变它的值了。 final 可以应用于局部变量两、实例变量和静态变量。不过,在定义 final 变量时,可以不必初始化。
对于可变的类,使用 final 修饰符可能会对读者造成混乱,如
private final Date hiredate; 仅仅意味着存储在 hiredate 变量中的对象引用在对象构造之后不能被改变,而并不意味着 hiredate 对象是一个常量,任何方法都可以对 hiredate 引用的对象调用 setTime 更改器。
     而对于不可变的类,用 final 修饰符修饰则以为着它作为一个常量,如 private final String name;
     (2)   static( 用法如: private static int nextId=1)
         类的所有实例共享同一个静态域,静态域属于类,而不属于任何对象。
     (3)   类常量,如
         public static final double PI=2.1415926;
     (4)   静态方法
         因为静态方法不能操作对象,所以不能在静态方法中访问实例域,但是,静态方法可以访问自身类中的静态域。如:
         public static int getNextID(){return nextID;}
         可以使用对象调用静态方法,但不建议这么用。
         每一个类 ( 不管类的权限如何 ) 都可以有一个 main 方法,用来对类进行测试。
7.    Java 中方法参数的使用情况
     (1)   一个方法不能修改一个基本数据类型的参数。
     (2)   一个方法可以改变一个对象参数的状态。
     (3)   一个方法不能让对象参数引用一个新的对象。
8.    方法重载:
     返回类型不是方法签名的一部分,也就是说,不能有两个名字相同,参数类型也相同却返回不同类型值的方法。
9.    构造函数
     (1)   如果类中提供了至少一个构造器,但是没有提供默认的构造器,那么在构造对象时若不提供构造参数就被视为不合法。默认构造函数中,数值型数据设置为 0 ,布尔型数据设置为 false ,所有对象变量将设置为 null 。如果希望所有域拥有默认值,则可以提供下列格式的构造器:
     public ClassName(){
 
}
     (2)   显示域初始化,如:
     class Employee{
         private String name= “” ;
}
域也可以通过调用方法来进行初始化:
class Employee{
     static int assignId(){
         int r=nextId;
         nextId++;
         return r;
}
private int id=assignId();
}
在 C++ 中,不能直接初始化实例域,所有的域必须在构造器中设置。但是,有一种特殊的初始化器列表语法,如:
Employee::Employee(String n, double s):name(n),salary(s){
 
}
(3)   一个构造器调用同一个类的另一个构造器,如:
public Employee(double s){
         this(“Emploee #”+nextId,s);
}
this 构造函数只能写在第一行。
Java 中的 this 等价于 C++ 的 this 指针,但是 C++ 中,一个构造器不能调用另一个构造器。
     (4)   初始化块,如:
         {
              id=nextId;
              nextId++;
}
调用构造器的具体处理步骤:
1)    所有数据域被初始化为默认值
2)    按照在类声明中出现的次序依次执行所有域初始化语句和初始化块
3)    如果一个构造器调用了另外一个构造器,则执行第二个构造器的主体。
4)    继续执行这个构造器的主体。
     (5)   静态域初始化:
         static int nextId=1 ;
         使用静态的初始化块:
         static{
              Rondom generator=new Random();
              nextId=generator.nextInt(1000);
}
     (6)   当参数名与实例域名相同时,实例域被屏蔽,此时可以通过隐式参数来初始化实例域,如:
     public Employee(String name,double salary){
         this.name=name;
         this.salary=salary;
}
10.   使用 Java 编写一个没有 main 方法的“ Hello, World ”程序:
     public class Hello{
         static{
              System.out.println(“Hello, World”);
}
}
在静态初始化块的尾部调用 System.exit(0) 来避免 main is not defined 的错误。
11.   类的导入 (import)
在所有的源代码的顶部,在 package 语句的后面。
(1)   采用两种方式访问另一个包中的公有类
     其一,添加完整的包名,如 java.util.Date today=new java.util.Date();
其二, import 语句导入一个特定的类或者整个包,如: import java.util.*;
或 import java.util.Date
     (2)   当导入的包中包含相同名称的类名时,使用这个类将出现编译错误,如:
         import java.util.*;
         import java.sql.*;
         使用 Date today=new Date() 时出错。
         此时,可以增加一个特定的 import 语句来解决这个问题,如
         import java.util.*;
         import java.sql.*;
         import java.util.Date;
         当需要同时使用两个包中的类时,则在每个类名前面加上完整的包名。
     Java 中的 import 和 package 相当于 C++ 中的 using 和 namespace 指令。
12.   静态导入
     添加一条指令, import static java.lang.System.*;
     则可以可用 System 类的静态方法和静态域,而不必加类名前缀。
     还可以导入特定的方法或域,如:
     import static java.lang.System.out;
     导入静态方法和静态域有两个实际的应用:
     1)    算数运算,如 import java.Math
         sqrt(x,2)+pow(y,2);
     2)    笨重的常量,如
         if(d.get(DAY_OF_WEEK)==MONDAY)…
13.   将类放入包中 (package)
     (1)   如果没有在源文件中放置 package 语句,那么源文件中的类奖被放置在一个默认包中。
     编译器对带有文件分隔符和扩展名 .java 的文件进行操作,而 java 解析器对带 . 分隔符的包进行操作。
     (2)   jre/lib 目录下的 rt.jar 包含数千个运行时的类。
(3)   在 UNIX 下,类路径的不同项目之间是采用:分割的, Windows 下采用;分隔,“ . ”表示当前目录。
类路径包括:
         1)    基目录,如 /home/user/classdir 或 c:\classes
         2)    当前目录
         3)    jar 文件,如 /home/user/archives/archive.jar 或 c:\archives\archive.jar
运行时库 ( 在 jre/lib 和 jre/lib/ext 目录下的 rt.jar 和一些其它的 jar 文件 ) 会被自动搜索,所以不必将它们显示地列在类路径中。
     类路径所列出的目录和归类文件是搜索类的起始点。
     (4)   设置类路径,两种方法:
         1)    为编译器和字节码制定 -classpath 选项,如:
              javac –classpath /home/user/classdir:./:/home/user/archives.jar MyProg.java
         也可以用 -cp 代替 -classpath 。
         2)    设置 CLASSPAT 环境变量。
              java5.0 以后不必设置类路径就可以编译当前目录的类。
              编译技巧: javac –d . Hello.java 将编译后的字节码文件直接加到 package 目录下。
(5)   javac 编译器总是在当前目录中查找文件,而 java 解释器仅在类路径中有“ . ”目录的时候,才查看当前目录。如果没有设置类路径,并不会产生什么问题,默认的类路径包含“ . ”目录。然而如果设置了类路径但忘记了包含“ . ”目录,程序仍然可以通过编译,但不能运行。
14.   包作用域
     对于方法和域,标记为 public 的部分可以被任意的类实用,标记为 private 的部分只能被定义在它们的类实用。如果没有指定 public 和 private ,那么这部分可以被同一个包中的所有类实用。
15.   文档注释
(1)   javadoc 应用程序 (utility) 从下面的一个特性抽取信息:
     包、公有类和接口、公有的和受保护的方法、公有的和受保护的域
     (2)   每个 /**…*/ 文档注释在标记之后紧跟着自由格式文本,标记以 @ 开始,如 @author 等。自由格式文本中,可以使用 HTML 修饰符,如用于强调的 , 用于设置等宽打字机的 , 用于着重强调的 以及包含图像的 等。但是,不要用


,因为他们与文档的格式产生冲突。
     (3)   如果文档中有到其它文件的链接,如图像文件,则应该把这些文件放到子目录 doc-files 中, javadoc 将从源目录拷贝这些目录及其中的文件到文档目录中。
     (4)   类注释:必须放在 import 语句之后,类定义之前。
     (5)   方法注释:必须放在所描述的方法之前
         常用标记:
         1)    @param variable description: 向当前方法的“ param ”部分添加一个条目,可以占据多行。
         2)    @return description : 向当前方法添加“ return ”部分
         3)    @throws class description : 表示方法可以抛出异常
         如:
              /*
                   Raises the salary of an employee
                   @param byPercent the percentage by which to raise the salary
                   @return the amount of the raise
*/
puboic double raiseSalary(double byPercent){
 
}
     (5)   域注释:只需要对公有域建立文档 ( 通常指静态常量 ), 如:
         /**
         The “Hearts” card suit
*/
public static final int HEARTS=1;
     (6)   通用注释
         下列标记用在类文档的注释中
         1)    @author name: 产生一个“ author ”条目
         2)    @version text 产生一个“ version ”条目
         下列标记用于所有的文档注释
         3)    @since text:text 可以使引入特性的版本描述,如 @since version 1.7.1
4)    @deprecated text 对类、方法或变量 增加一个不再使用的注释 , text 给出了取代的建议,如 :   @deprecated Use setVisible(true) instead
     通过使用 @see 和 @link 标记,可以使用超级链接,链接到 javadoc 文档的相关部分或外部文档。
         5)    see reference: 将在 see also 部分添加一个超级链接,可以用于类、方法中。
              reference 可以选择下列情形之一:
              1_    package.class#feature label
如: @see com.horseman.Employee#raiseSalary(double)
              2_    label
                   如: @see The core home page
              如果省略了 label ,用户看到的锚的名称就是目标代码或 URL 。
              3_    “text”
                   如: @see “core java2”
         具有以 @ 开头的必须放在一起。
         @link 与 @see 同理。
     (7)   包与概述注释
         要想产生包注释,就需要在每一个包目录中添加一个 package.html 的文件。在标记 … 之间的所有文本都会被抽取出来。还可以为所有的源文件提供一个概述性的注释,这个注释被放置在一个名为 overview.html 的文件中,该文件为于所有包含源文件的父目录中。
     (8)   注释的抽取
         在源文件目录,执行下列命令:
         javadoc –d docDirectory nameOfPackage,nameOfPackage,…
         如果在默认包中,就应该执行:
         javadoc –d docDirectory *.java
         如果省略 -d docDirecotry ,则 html 文件提取到当前目录。
16.   类的设计技巧
     1)    将数据设计为私有
     2)    一定要对数据初始化。 Java 不对局部变量进行初始化,但是会对对象的实例域进行初始化。
     3)    不要在类中使用过多的基本数据类型,可以用其他的类代替多个相关的基本数据类型的使用。
     4)    将职责过多的类进行分解。

编程百科
2020-08-27 12:58:55
相关推荐: 如何进行表达式求值,就如Javascript中的eval 指针与数组的异同 JavaScript内核系列 第5章 数组 书评:Java核心编程卷1——基础 Java的OO特性 byte[]与各种数据类型互相转换示例 Java为什么基本数据类型不需要进行创建对象? Java变量类型识别的3种方式
推荐群组: JAVA 3T
更多相关推荐
OO
1.    Java 整形
类型 存储需求 取值范围 int 4 字节 -2 147 483 648 ~ 2 147 483 647 short 2 字节 -32 768 ~ 32767
long
byte
8 字节
1 字节
...
-128~127
 
长整形有一个后缀 L ,十六进制有一个前缀 0x ,八进制有有一个前缀 0 。
浮点型有两种: float ( 4 字节), double ( 8 字节)。小数默认为 double 类型,可以在小数后加 D 表示,当小数后标注 F 时表示 float 类型。所以将一个小数赋值给一个浮点变量时,无法通过编译,需要强制转化或者在小数后加 F 。
命令 System.out.println(2.0-1.0); 打印出 0.8999999999999999 ,而不是 0.9 ,因为浮点数值是采用二进制系统表示的,而在二进制系统中无法精确表示 1/10 ,就像十进制无法表示 1/3 一样。
char(16 位 ) , boolean 只有两个值,区别于 C++ ,当把非 true 和 false 的值赋值给 boolean 变量时,编译通不过。
2.    特殊字符的转义序列符
转义序列 名称 Unicode 值 \b 退格  \t 制表 \n 换行
\r 回车 \” 双引号 "
\’
\\
单引号
反斜杠
'
\
3.    判定 Unicode 字符是否属于字母的方法可以使用 Character 中的 JavaIdentifierStart 和  JavaIdentifierPart 方法。
4.    常量用 final 修饰;类常量用 static final 设置,可以在一个类中的多个方法中使用;如果类常量被设置为 public ,则其他类的方法也可以使用这个常量。类常量定义于 main 方法的外部,使用方法如:
     public class Constants2{
         public static void main(String[] args){
              double paperWidth=8.5;
              double paperHeight=11;
              System.out.println(“Paper size in centimeters:”+
paperWidth*CM_PER_INCH+”by”+paerHeight*CN_PER_INCH);
}
public static final double CM_PER_INCH=2.54;
}
5.    三元运算符的表示方法: x6.    从 JDK5.0 开始,不必再数学方法名前添加前缀“ Math. ” , 而只需在头文件的顶部添加下列代码就可以了: import static java.long.Math.( 静态导入 )
7.    数值之间的合法转换
byte à short à int à long à float à double
char à int
强制类型转化的语法格式是在圆括弧中给中想要转化的目的类型,随后紧跟待转化的变量名。如,
double x=9.997
int nx=(int)x;
8.    运算符优先级 ( 从上到下递减,同行相同 )
[].()( 方法调用 ) ! ++ -- +/-( 一元运算 ) () 强制类型转换 new * / % + - << >> >>> < <= > >= instanceof == != & ^ | && ||
?:
= += -= *= /= %= |= ^= <<= >>= >>>=
9.    String
     (1)   构造方法
         1)    构造空字符串 String();
         2)    利用字节数组构造 String(byte[] bytes);
         3)    利用字符数组构造 String(byte[] chars);
     (2)   比较
compareTo(String anotherString) :大于参数字符串则返回一个大于 0 的值, = 则返回 0 ,小于则返回一个小于 0 的值
         compareToIgnoreCase(String str); 不考虑大小比较两个字符串
         equals(Object anObject): 比较字符串与制定的对象
         equalsIgnoreCase(String anotherString): 不考虑大小比较
     (3)   索引
         indexOf(int ch): 返回制定字符在此字符串第一次出现处的索引
         indexOf(int ch, int fromIndex);
         lastIndexOf 返回最后一次出现的指定字符在此字符串中的索引
         indexOf(String str): 返回第一次出现的制定字符串中的索引
         indexOf(String str, int fromIndex);
     (4)   返回长度
         length();
     (5)   替换
         String repalce(char oldChar, char newChar) 用 newChar 替换此字符串中出现的所有 oldChar
         Sting replaceAll(String regex, String replacement) 使用给定的 replacement 字符串替换此字符串与正则表达式匹配的每个子字符串
         String replaceFirst(String regex,Stirng replacement)
         String[] split(String regex) 根据正则表达式的匹配来拆分此字符串
         String[] split(String regex, int limit) 其中, limit 参数控制模式应用的次数,因此影响结果数组的长度。如果 limit>0 ,则模式将被应用 n-1 次,数组的长度将不会大于 n ,数组的最后项将包含超出最后匹配的定界符的所有输入。如果 limit 为非正,模式将被应用尽可能多的次数,而且数组可以是任意长度。
     (6)   匹配
         endsWith(String suffic): 测试此字符串是否以指定的后缀结束
         startsWith(String prefix); 测试此字符串是否以之地鞥的前缀开始
         startsWith(String prefix, int toffset) 从指定索引开始, ...
     (7)   子串
         subString(int beginIndex, int endIndex) 返回所以区间 [beginIndex, endIndex) 的子串
         subString(int beginIndex) 返回 beginIndex 到末尾的子串
     (8)   编辑
         char[] toCharArray() 将此字符串转化为一个新的字符数组。
          String toUpperCase()
         String toLowerCase()
         trim() 返回字符串的副本,忽略前导空白和尾部空白
     (9)   值传递
         String valueOf(char c);
         String valueOf(char[] data, int offset, int count); 其中 offset 为子数组的第一个字符的索引, count 参数为 Stirng 值的长度。
     (10) 检测字符串是否相等
         s.equal(t), 其中 s 和 t 既可为常量也可为变量。不区分大写写比较可用函数
s.equalIgnoreCase(t).
== 运算符用来检测的是否放在同一位置,
字符串常量是共享的,即所有的字符串常量放在同一个存储池中。
10.   Swing 对话框的使用 (java.swing.*)
     String input=JOptionPane.showInputDialog(promptString);
     返回值是用户输入的字符串, promptString 为提示信息。如果需要返回的数值或 Double ,可以通过 Integer.parseInt(input); 或 Double.parseDouble(input).
11.   在 c++ 中,可以在嵌套的块中重定义一个变量,在内层定义的变量会覆盖在外层定义的变量。在 Java 中不允许这样的重定义。
     在 for 循环的初始段定义的变量的作用范围是整个 for 循环。
     switch 的语法格式:
     switch(choice){
         case 1:
              …
              break;
         default:
              …
              break;
}
特殊的 break 与 continue 的用法:
label:
{
     if(condition) break label;
}
//jump here when the break statement executes
当 label: 的后面没有 {} 时,则跳转后它的后一个语句块,入 if 执行后面的语句一样。 continue 的用法同理。
12.   大数值
     BigInteger 和 BigDecimal(java.math) 可处理任意长度的数值,其中 BigInteger 实现了任意精度的整数运算, BigDecimal 实现了任意精度的浮点数运算。
     (1)   将普通的数值转化为大数值,如 BigInteger a=BigInteger.valueOf(100);
     (2)   不能使用普通的运算符号,替代为:
         add,subtract,mutiply,divide,mod
     (3)   比较: compareTo, 相等返回 0 ,小于返回负数,大于返回整数。
13.   数组
(1)   申明数组的两种形式:
          int[] a=new int[100]; 或者 int a[]=new int[100];
          获取数组中的元素个数: a.length
          for each 循环:
         语法:    for(int element:a){
    
}
         其中, a 是数组或者是实现了 iterator 的类如( ArrayList )
     (2)   java 中的数组相当于 C++ 中的数组指针,不过只能做 [] 运算,不能对数组变量作 + 运算来取地址。
(java.lang.)System.arrayCopy(from,fromIndex,to,toIndex,count); 方法实现将一个数组 from 从索引 fromIndex 的 count 个元素拷贝到 to 的以索引 toIndex 为下表的后面。
     (3)   Arrays.sort(a);// 实现数组的从小到大排序。
         (java.util.)Arrays 提供的其他常用静态方法:
1)    static int binarySerch(type[]a,type v)
erayutil.();x.y() ;
     使用二分查找自 a 中找 v 。
     2)    static void fill(type[] a, type v);
         将数组的所有元素设置为 v 。
     3)    static   boolean equals(type[] a, type[] b);
         如果两个数组的长度相同,并且下表相同的元素都对应相等,则返回 true 。
     (4)   数组初始化以及匿名数组
         初始化 :int smallPrimers={1,2,3};
         申明匿名数组: new int[]{1,2,3}
         可以将一个匿名数组赋值给一个数组变量: int smallPrimer=new int[]{1,2,3}
14.   (1)   二维数组的申明:
         double[][] balances;
         balances=new double[NYEARS][NRATES];
     (2)   二维数组的初始化:
         int[][] magicSqure=
{
     {16,3,2},
     {4,15,4}
};
     (3)   由于可以单独地存取数组的某一行,所以可以对两行进行交换或其他操作。
     (4)   当需要创建不规则的数组时,可以单独分配各行的空间。如:
         int[][] olds=new int[MMAX+1][];
         for(int n=0;n<=MMAX;n++){
              odds[n]=new int[n+1];
}
编程百科
2020-08-27 12:58:42
import java.util.ArrayList; import java.util.Collections; import java.util.List; public class Ball { private List redBalls; private Integer blueBall; public Ball() { calculateRedBalls(); calculateBlueBall(); } private void calculateRedBalls() { List balls = new ArrayList(); for (int i = 1; i <= 33; i++) { balls.add(i); } Collections.shuffle(balls); redBalls = new ArrayList(balls.subList(0, 6)); Collections.sort(redBalls); } private void calculateBlueBall() { blueBall = (int) (Math.random() * 16 + 1); } @Override public String toString() { return "red balls is " + redBalls + " and blue ball is " + blueBall; } public static void main(String[] args) { Ball ball = new Ball(); System.out.println(ball); } }
编程百科
2020-08-27 12:58:35
public class A{ protected void protectedHello() { System.out.println("protected hello world!"); } private void privateHello() { System.out.println("private hello world!"); } } public class B{ public static void main(String [] args) { A a = new A(){{ this.protectedHello(); this.privateHello(); }}; } } {{}}内等于在constructor后加东西,同理还可以做override。JMock的record里面有大量使用。 上面的put等于是new了一个hashmap之后立马在里面put了两个东东而已。
编程百科
2020-08-27 12:58:14
相关推荐: Spring+hibernate+DWR整合 关于Spring 2.5 和 Hibernate 3 基于Annotation的集成 我们还(要)在用jdbc吗? 结构化编程与面向对象编程的概念区别 结构化编程与面向对象化编程的区别 结构化方法和面向对象方法的比较 结构化设计和面向对象设计 结构化方法、面向对象方法的区别
推荐群组: struts2
更多相关推荐
OO
本文所讨论的话题
通常在一个业务系统中会有各种不同的角色,而在系统的若干功能模块中,这些角色所能看到的数据是不一样的。那么在程序中如何处理类似问题会更优呢,本文想通过一个简单的场景来和大家再次探讨一下如何用OO来改善我们的系统。
 
场景
 系统中目前有三种角色,超级管理员、管理员、普通用户。有一个功能是显示书籍列表,每一种角色所能看到的书籍是不同的。这里面会有一些规则,但是这些规则不是文本所要讨论的重点。
 
最初的实现
通常我们最快能想到的思路是,先创建两个类,用户类User、书籍类Book。在User类中我们创建了判断用户角色类型的方法。 package fangwei.solution1.user.domain; public class User { public static final int ROLE_SUPER_ADMIN = 1; public static final int ROLE_ADMIN = 2; public static final int ROLE_COMMON_USER = 3; private Integer roleId; public void setRoleId(Integer roleId) { this.roleId = roleId; } public Integer getRoleId() { return roleId; } public boolean isSuperAdmin() { return this.roleId==ROLE_SUPER_ADMIN; } public boolean isAdmin() { return this.roleId==ROLE_ADMIN; } public boolean isCommonUser() { return this.roleId==ROLE_COMMON_USER; } }
  package fangwei.solution1.book.domain; public class Book { }
 
然后用分层的方式去处理问题,先创建service层的接口。为了处理不同角色的情况,我们传入了一个user对象。 package fangwei.solution1.book.service; import java.util.List; import fangwei.solution1.book.domain.Book; import fangwei.solution1.user.domain.User; public interface BookService { public List listBook(User user); }
  
实现service层的接口,同时我们需要创建dao层的接口 package fangwei.solution1.book.service; import java.util.List; import fangwei.solution1.book.dao.BookDao; import fangwei.solution1.book.domain.Book; import fangwei.solution1.user.domain.User; public class BookServiceImpl implements BookService { private BookDao bookDao; public void setBookDao(BookDao bookDao) { this.bookDao = bookDao; } public List listBook(User user){ List result = null; if(user.isSuperAdmin()){ result = bookDao.selectBookList4SuperAdmin(); }else if(user.isAdmin()){ result = bookDao.selectBookList4Admin(); }else if (user.isCommonUser()) { result = bookDao.selectBookList4CommonUser(); } return result; } }
  package fangwei.solution1.book.dao; import java.util.List; import fangwei.solution1.book.domain.Book; public interface BookDao { public List selectBookList4SuperAdmin(); public List selectBookList4Admin(); public List selectBookList4CommonUser(); }
 
实现dao层的接口,为了本文的讨论,我们加入了log语句,而并没有真正去编写实现代码 package fangwei.solution1.book.dao; import org.apache.log4j.Logger; import java.util.List; import fangwei.solution1.book.domain.Book; public class BookDaoImpl implements BookDao { private static final Logger logger = Logger.getLogger(BookDaoImpl.class); public List selectBookList4SuperAdmin() { logger.debug("selectBookList4SuperAdmin"); return null; } public List selectBookList4Admin() { logger.debug("selectBookList4Admin"); return null; } public List selectBookList4CommonUser() { logger.debug("selectBookList4CommonUser"); return null; } }
  
好了,下面我们来编写一个测试,来看service层的实现是否正确 package fangwei.solution1.book.service; import org.junit.After; import org.junit.Before; import org.junit.Test; import fangwei.solution1.book.dao.BookDaoImpl; import fangwei.solution1.book.service.BookServiceImpl; import fangwei.solution1.user.domain.User; public class TestBookServiceImpl { private BookServiceImpl bookService; @Before public void setUp() throws Exception { bookService = new BookServiceImpl(); bookService.setBookDao(new BookDaoImpl()); } @After public void tearDown() throws Exception { } @Test public void testListBook() { User user = new User(); user.setRoleId(User.ROLE_SUPER_ADMIN); bookService.listBook(user); user.setRoleId(User.ROLE_ADMIN); bookService.listBook(user); user.setRoleId(User.ROLE_COMMON_USER); bookService.listBook(user); } }
 
运行这个测试,输出结果如下 selectBookList4SuperAdmin selectBookList4Admin selectBookList4CommonUser
 
说明这个实现是正确的。
问题在哪里
在一个业务系统中类似上面的场景会有很多,也就是说我们会在service层的若干实现方法中使用if...else if...else if...用来处理不同角色的情况。如果角色类型是不会增加的,那么上面的实现没有任何问题。但是很可惜,客户在使用系统一段时间后提出要增加新的角色类型,当然新角色所能看到的书籍列表也是有别于之前角色的:(
这个时候,我们痛苦的发现,我们需要满系统去找使用if...else if...else if...用来处理不同角色的地方,然后再加入一个else if。有人会说,这没什么啊,我们的团队是按功能模块划分来开发的,每个人挨个service类去加就好了。但是我当时的想法是,一定有比这更好的设计。
另一种实现
我们再回过头去看service层的listBook方法,其实我们要做的事只有一件——查询书籍列表,只是因为角色这个因素导致出现了分支结构。那么我们可以将这个变化抽象出来,使我们在查询书籍列表时不需要关心角色这个因素。下面是另一种实现的service层接口,同时我们抽象出了新的接口BookBiz package fangwei.solution2.book.service; import java.util.List; import fangwei.solution2.book.domain.Book; import fangwei.solution2.book.domain.BookBiz; public interface BookService { public List listBook(BookBiz bookBiz); }
  package fangwei.solution2.book.domain; import java.util.List; import fangwei.solution2.book.domain.Book; public interface BookBiz { public List listBook(); }
 
这样,我们在实现service层接口的时候就不需要考虑角色因素了 package fangwei.solution2.book.service; import java.util.List; import fangwei.solution2.book.domain.Book; import fangwei.solution2.book.domain.BookBiz; public class BookServiceImpl implements BookService { public List listBook(BookBiz bookBiz) { return bookBiz.listBook(); } }
 
 那么我们如何使用BookBiz这个接口呢,我们可以创建三个新的用户类分别对应于场景中三种不同的角色,然后让他们实现BookBiz这个接口。为了测试的简单,我们直接依赖了dao层的实现BookDaoImpl,在实际当中可以使用spring等ioc容器在运行期注入。
  package fangwei.solution2.user.domain; import java.util.List; import fangwei.solution2.book.dao.BookDao; import fangwei.solution2.book.dao.BookDaoImpl; import fangwei.solution2.book.domain.Book; import fangwei.solution2.book.domain.BookBiz; public class SuperAdminUser extends User implements BookBiz { BookDao bookDao = new BookDaoImpl(); public List listBook() { return bookDao.selectBookList4SuperAdmin(); } }
 
  package fangwei.solution2.user.domain; import java.util.List; import fangwei.solution2.book.dao.BookDao; import fangwei.solution2.book.dao.BookDaoImpl; import fangwei.solution2.book.domain.Book; import fangwei.solution2.book.domain.BookBiz; public class AdminUser extends User implements BookBiz { BookDao bookDao = new BookDaoImpl(); public List listBook() { return bookDao.selectBookList4Admin(); } }
 
  package fangwei.solution2.user.domain; import java.util.List; import fangwei.solution2.book.dao.BookDao; import fangwei.solution2.book.dao.BookDaoImpl; import fangwei.solution2.book.domain.Book; import fangwei.solution2.book.domain.BookBiz; public class CommonUser extends User implements BookBiz { BookDao bookDao = new BookDaoImpl(); public List listBook() { return bookDao.selectBookList4CommonUser(); } }
 
 依然需要写一个测试来验证我们的设计
  package fangwei.solution2.book.service; import static org.junit.Assert.*; import org.junit.After; import org.junit.Before; import org.junit.Test; import fangwei.solution2.book.domain.BookBiz; import fangwei.solution2.book.service.BookService; import fangwei.solution2.book.service.BookServiceImpl; import fangwei.solution2.user.domain.AdminUser; import fangwei.solution2.user.domain.CommonUser; import fangwei.solution2.user.domain.SuperAdminUser; public class TestBookServiceImpl { private BookService bookService; @Before public void setUp() throws Exception { bookService = new BookServiceImpl(); } @After public void tearDown() throws Exception { } @Test public void testListBook() { BookBiz bookBiz = new SuperAdminUser(); bookService.listBook(bookBiz); bookBiz = new AdminUser(); bookService.listBook(bookBiz); bookBiz = new CommonUser(); bookService.listBook(bookBiz); } }
 运行测试的输出结果同第一种实现 selectBookList4SuperAdmin selectBookList4Admin selectBookList4CommonUser
 
上层如何调用
通常我们都会将User对象放入HttpSession对象中,所以要使用此种实现,我们需要在用户登录成功后,根据不同的角色创建不同的用户类放入HttpSession对象中。登录的代码在这里就略过了,下面给出场景的一种action层实现及测试代码,重点是对HttpSession对象的操作,框架是次要因素 package fangwei.solution2.book.action; import java.util.List; import java.util.Map; import org.apache.struts2.convention.annotation.Action; import org.apache.struts2.convention.annotation.Result; import org.apache.struts2.interceptor.SessionAware; import com.opensymphony.xwork2.ActionSupport; import fangwei.solution2.book.domain.Book; import fangwei.solution2.book.domain.BookBiz; import fangwei.solution2.book.service.BookService; public class BookAction extends ActionSupport implements SessionAware{ private static final long serialVersionUID = 2744158510001123482L; private List bookList; private Map session; private BookService bookService; public void setBookList(List bookList) { this.bookList = bookList; } public List getBookList() { return bookList; } public void setSession(Map session) { this.session = session; } public void setBookService(BookService bookService) { this.bookService = bookService; } @Action(value="/book/listBook", results = {@Result(name=SUCCESS,location="/WEB-INF/jsp/book/listBook.jsp")} ) public void listBook() { BookBiz bookBiz = (BookBiz) session.get("user"); bookList = bookService.listBook(bookBiz); } }
  package fangwei.solution2.book.action; import static org.junit.Assert.*; import java.util.HashMap; import java.util.Map; import org.junit.After; import org.junit.Before; import org.junit.Test; import fangwei.solution2.book.service.BookServiceImpl; import fangwei.solution2.user.domain.AdminUser; import fangwei.solution2.user.domain.CommonUser; import fangwei.solution2.user.domain.SuperAdminUser; import fangwei.solution2.user.domain.User; public class TestBookAction { private BookAction bookAction; @Before public void setUp() throws Exception { bookAction = new BookAction(); bookAction.setBookService(new BookServiceImpl()); } @After public void tearDown() throws Exception { } @Test public void testListBook() { Map session = new HashMap(); bookAction.setSession(session); User user = new SuperAdminUser(); session.put("user", user); bookAction.listBook(); user = new AdminUser(); session.put("user", user); bookAction.listBook(); user = new CommonUser(); session.put("user", user); bookAction.listBook(); } }
 
好处在哪里
再回到第一种实现提出的问题,现在如果我们需要增加新的角色类型,就可以创建一个新的用户类XXXUser,然后实现BookBiz等需要的业务接口就可以了。在这种设计下,我们再也不需要满系统去增加else if了。同样的一句代码bookService.listBook(bookBiz);在运行期会有不同的执行效果,这就是OO的多态之一。有人会说,看起来service层的实现没啥用了,只是一个facade了。其实不然,BookBiz抽象出来的是由于角色因素导致的变化,我们依然可以在service中编写所有角色共有的后续业务逻辑,而不必在每个BookBiz的实现中去重复。 package fangwei.solution2.book.service; import java.util.List; import fangwei.solution2.book.domain.Book; import fangwei.solution2.book.domain.BookBiz; public class BookServiceImpl implements BookService { public List listBook(BookBiz bookBiz) { List listBook = bookBiz.listBook(); //后续的业务逻辑 //... return listBook; } }
 
疑惑依然存在
第二种实现隔离了由于角色因素导致的变化,使我们能够更方便的增加新的角色类型,但是当需要隔离的变化多了以后会怎么样呢。我们的BookBiz接口中的方法会越来越多,类似BookBiz的接口会越来越多,但是我们的每一种用户类如SuperAdminUser只有一个,那么最后我们的SuperAdminUser类实现的接口会越来越多,即实现的接口方法会越来越多,这个类会不断的膨胀下去导致维护困难。。。这种情况,我们又该如何应对呢?一定还有更优的设计?本文抛砖引玉,旨在了解大家在遇到类似问题时如何用OO的思想去分析解决。
 
编程百科
2020-08-27 12:57:58
public class B { public static void main(String Args[]){ int i =1 ,j =0; switch(i){ default:j+=2; case 2:j+=6; case 4:j+=6; case 0:j+=4; } System.out.println(j); } } 这样就不难理解输出结果18了 switch 每次搞懂了记住了,然后又混了,特别default 和break 组合很乱的时候, 记住进入点,和 没break 顺序进行,感觉思路清晰多了
编程百科
2020-08-27 12:57:53
相关推荐: Js和Groovy的语言层面上的比较,兼谈“Javaer眼中的Js” 隐式转换:比动态类型更强大? python 与 ruby 之间的区别 java 入门书籍(java7) 组件化、模块化、集中式、分布式、服务化、面向服务的架构、微服务架构 基本Java资源
推荐群组: Python
更多相关推荐
OO
1, 最好加上 return
 
groovy 方法(函数)在没有return语句的情况下,
默认把最后一条语句的求值结果作为返回值,但在带有 条件语句 的情况下
似乎有点让人迷惑,看下面的例子(判断一个对象是不是字符串):
  def isString(str){ if(String.class.isInstance(str)){ "YES" }else{ "NO" } }  
 运行脚本 println isStr("hello")
 打印出(Groovy 1.6以前):  null
 不过在Groovy 1.6以后将会打印出 YES ,
 
修改方案(1):
 
  def isString(str){ String.class.isInstance(str)?"YES":"NO" }
 
修改方案(2):
  de isString(str){ if(String.class.isInstance(str)){ return "YES" }else{ return "NO" } }
 
个人感觉:如果不是实在懒得敲那几个字符,最好加上return,这样也会使程序的结构更加清楚。
 
2, 方法(函数)调用的形式:
  //第二个参数是一个函数 def static func(arg,callback){ callback(arg) }
 
运行下面的三行语句
  func("hello",{println it}) func("hello"){println it} func "hello",{println it}
 
都会打印出: hello
调用一个方法(函数)就有三种方式,
我不讨论那种方式比较好,
但总感觉第二种调用方式怪怪的。。
看来Groovy的代码风格更是会千差万别了。
 
 
3, def 关键字
def 关键字用来定义方法,属性和本地变量。
 
def 定义的方法 默认 为 public java.lang.Object ×××()
如:
def func(){...}
对应的java方法为
public java.lang.Object func()
 
def定义的属性和本地变量 默认 为 null,赋值之后为
指向具体的对象。
 
4, Groovy不支持的一些java特性
(1)Groovy 不支持 内部类
(2)Groovy 不支持 java创建数组的方式
如 def ints = new int[]{1,2,3},
暂列两条。。
 
 
 
 
编程百科
2020-08-27 12:57:41
visitor 写道
好像 JAVA 在新版本中 没有引入闭包
因为 引入闭包 这项工作太复杂
好像是这样 , 反正我觉得没必要 呵呵
编程百科
2020-08-27 12:57:28