A. java時鍾設計
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Container;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.MediaTracker;
import java.awt.Toolkit;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Line2D;
import java.awt.image.BufferedImage;
import java.net.URL;
import java.text.DateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import javax.swing.JFrame;
import javax.swing.JLabel;
/**
* 時鍾程序
*/
public class Clock extends JFrame
{
// 今天的日期對象
private GregorianCalendar now = new GregorianCalendar();
// 時鍾標簽,上面畫的是圓形時鍾
private ClockLabel clockLabel = new ClockLabel();
// 星期標簽,指示星期
private JLabel weekLabel = new JLabel();
// 日期標簽,指示日期
private JLabel dateLabel = new JLabel();
// 品牌標簽
private JLabel remarkLabel = new JLabel();
// 時間標簽,指示時間
private JLabel timeLabel = new JLabel();
public Clock()
{
// 設置主界面屬性
setTitle("時鍾");
setSize(500, 480);
setLocation(100, 100);
init();
setResizable(false);
}
private void init()
{
// 初始化品牌標簽
remarkLabel.setText("MyClock");
remarkLabel.setLocation(225, 80);
remarkLabel.setSize(100, 30);
remarkLabel.setFont(new Font("Bookman Old Style", Font.BOLD, 15));
remarkLabel.setForeground(Color.darkGray);
// 初始化星期標簽
weekLabel.setSize(60, 20);
weekLabel.setLocation(315, 190);
weekLabel.setForeground(Color.darkGray);
weekLabel.setFont(new Font("Arial Narrow", Font.BOLD, 12));
// 為星期標簽賦值
int week = now.get(Calendar.DAY_OF_WEEK);
switch (week)
{
case 1:
weekLabel.setText("SUNDAY");
break;
case 2:
weekLabel.setText("MONDAY");
break;
case 3:
weekLabel.setText("TUESDAY");
break;
case 4:
weekLabel.setText("WEDNESDAY");
break;
case 5:
weekLabel.setText("THURSDAY");
break;
case 6:
weekLabel.setText("FRIDAY");
break;
case 7:
weekLabel.setText("SATURDAY");
break;
}
// 初始化日期標簽
dateLabel.setSize(20, 20);
dateLabel.setLocation(370, 190);
dateLabel.setForeground(Color.darkGray);
dateLabel.setFont(new Font("Fixedsys", Font.BOLD, 12));
// 設置日期標簽的值
dateLabel.setText("" + now.get(Calendar.DATE));
// 初始化時間標簽
timeLabel.setSize(500, 30);
timeLabel.setLocation(100, 400);
timeLabel.setForeground(new Color(0, 64, 128));
timeLabel.setFont(new Font("Fixedsys", Font.PLAIN, 15));
// 將各組件加入到主窗口中
Container con = getContentPane();
con.setBackground(Color.white);
con.setLayout(null);
con.add(weekLabel);
con.add(dateLabel);
con.add(remarkLabel);
con.add(timeLabel);
con.add(clockLabel);
}
public static void main(String[] args)
{
Clock clock = new Clock();
clock.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
clock.setVisible(true);
}
// 自定義時鍾標簽,畫一個圓形的時鍾
class ClockLabel extends JLabel implements Runnable
{
// 時鍾標簽的寬度和高度
private final int WIDTH = 500;
private final int HEIGHT = 440;
// 圓形時鍾的X半徑和Y半徑
private final int CIRCLE_X_RADIUS = 150;
private final int CIRCLE_Y_RADIUS = 155;
// 圓形時鍾的原點
private final int CIRCLE_X = 250;
private final int CIRCLE_Y = 200;
// 圓形時鍾指針的長度
private final int HOUR_LENGTH = 50;
private final int MIN_LENGTH = 80;
private final int SEC_LENGTH = 100;
// 當前時針所處的角度
double arcHour = 0;
// 當前分針所處的角度
double arcMin = 0;
// 當前秒針所處的角度
double arcSec = 0;
// 顏色的透明度,
// 當顏色的alpha值為1或者255時,表示不透明,其他值時透明
int alpha = 50;
// 標識顏色透明度變化的方向,
// 為true時表示越來越透明,為false時表示月來越不透明
boolean flag = false;
// 背景圖片的id,輪換顯示背景圖片時使用
int imageID = 0;
// 背景圖片對象數組
Image img[] = new Image[5];
// 背景圖片URL
URL url[] = new URL[] { ClockLabel.class.getResource("image/1.jpg"), ClockLabel.class.getResource("image/2.jpg"), ClockLabel.class.getResource("image/3.jpg"), ClockLabel.class.getResource("image/4.jpg"), ClockLabel.class.getResource("image/5.jpg"), ClockLabel.class.getResource("image/6.jpg") };
// 一個具有緩沖區的圖像對象
BufferedImage bufferedImage = null;
int imageSize = 2 * Math.max(CIRCLE_X_RADIUS, CIRCLE_Y_RADIUS);
// 為bufferedImage創建的Graphics對象,用於在bufferedImage上畫圖
Graphics bufferedImageGraphics = null;
// 時鍾線程,因為時鍾每秒鍾都要動一次,所以用線程
Thread clockThread = null;
// 構造方法
public ClockLabel()
{
// 設置時鍾標簽的大小
this.setSize(WIDTH, HEIGHT);
// 獲取時針、分針、秒針當前的角度
arcHour = now.get(Calendar.HOUR) * 30; // 一格30度
arcMin = now.get(Calendar.MINUTE) * 6; // 一格6度
arcSec = now.get(Calendar.SECOND) * 6; // 一個6度
// 根據圖片URL創建圖片對象
Toolkit tk = this.getToolkit();
img[0] = tk.createImage(url[0]);
img[1] = tk.createImage(url[1]);
img[2] = tk.createImage(url[2]);
img[3] = tk.createImage(url[3]);
img[4] = tk.createImage(url[4]);
try
{
// 使用MediaTracker載入圖片對象
// MediaTracker 類是一個跟蹤多種媒體對象狀態的實用工具類,
// 媒體對象可以包括音頻剪輯和圖像,但目前僅支持圖像.
MediaTracker mt = new MediaTracker(this);
mt.addImage(img[0], 0);
mt.addImage(img[1], 0);
mt.addImage(img[2], 0);
mt.addImage(img[3], 0);
mt.addImage(img[4], 0);
// 載入媒體跟蹤器中所有的圖像。
mt.waitForAll();
}
catch (Exception e)
{
e.printStackTrace();
}
// 創建一個有緩沖的Image對象
bufferedImage = new BufferedImage(imageSize, imageSize, BufferedImage.TYPE_INT_ARGB);
// 為BufferedImage創建Graphics2D對象,
// 以後用該Graphics2D對象畫的圖都會畫在BufferedImage中
bufferedImageGraphics = bufferedImage.createGraphics();
// 啟動線程
clockThread = new Thread(this);
clockThread.start();
}
public void paint(Graphics g1)
{
// Graphics2D繼承Graphics,比Graphics提供更豐富的方法
Graphics2D g = (Graphics2D) g1;
/** ***畫圓形時鍾的刻度,每6度便有一個刻度**** */
for (int i = 0; i < 360; i = i + 6)
{
// 設置畫筆的顏色為藍色
g.setColor(Color.blue);
// 設置畫筆的寬度為2
g.setStroke(new BasicStroke(2));
if (i % 90 == 0)
{
// 對於0,3,6,9點位置,使用一個大的藍色刻度
g.setStroke(new BasicStroke(5));// 畫筆寬度為5
// 當起點和終點一樣時,畫的就是點
g.drawLine(CIRCLE_X + (int) (Math.cos(i * Math.PI / 180) * CIRCLE_X_RADIUS), CIRCLE_Y + (int) (Math.sin(i * Math.PI / 180) * CIRCLE_Y_RADIUS), CIRCLE_X + (int) (Math.cos(i * Math.PI / 180) * CIRCLE_X_RADIUS), CIRCLE_Y + (int) (Math.sin(i * Math.PI / 180) * CIRCLE_Y_RADIUS));
}
else if (i % 30 == 0)
{
// 如果角度處於小時的位置,而且還不在0,3,6,9點時,畫紅色的小刻度
g.setColor(Color.red);
g.drawLine(CIRCLE_X + (int) (Math.cos(i * Math.PI / 180) * CIRCLE_X_RADIUS), CIRCLE_Y + (int) (Math.sin(i * Math.PI / 180) * CIRCLE_Y_RADIUS), CIRCLE_X + (int) (Math.cos(i * Math.PI / 180) * CIRCLE_X_RADIUS), CIRCLE_Y + (int) (Math.sin(i * Math.PI / 180) * CIRCLE_Y_RADIUS));
}
else
{
// 其他位置就畫藍色的小刻度
g.drawLine(CIRCLE_X + (int) (Math.cos(i * Math.PI / 180) * CIRCLE_X_RADIUS), CIRCLE_Y + (int) (Math.sin(i * Math.PI / 180) * CIRCLE_Y_RADIUS), CIRCLE_X + (int) (Math.cos(i * Math.PI / 180) * CIRCLE_X_RADIUS), CIRCLE_Y + (int) (Math.sin(i * Math.PI / 180) * CIRCLE_Y_RADIUS));
}
}
/** ****** 畫時鍾的指針 ******** */
// 畫時針
Line2D.Double lh = new Line2D.Double(CIRCLE_X, CIRCLE_Y, CIRCLE_X + Math.cos((arcHour - 90) * Math.PI / 180) * HOUR_LENGTH, CIRCLE_Y + Math.sin((arcHour - 90) * Math.PI / 180) * HOUR_LENGTH);
// 設置畫筆寬度和顏色
g.setStroke(new BasicStroke(6));
g.setColor(new Color(0, 128, 0));
// 利用Graphics2D的draw方法畫線
g.draw(lh);
// 畫分針
Line2D.Double lm = new Line2D.Double(CIRCLE_X, CIRCLE_Y, CIRCLE_X + Math.cos((arcMin - 90) * Math.PI / 180) * MIN_LENGTH, CIRCLE_Y + Math.sin((arcMin - 90) * Math.PI / 180) * MIN_LENGTH);
g.setStroke(new BasicStroke(3));
g.setColor(new Color(0, 128, 192));
g.draw(lm);
// 畫秒針
Line2D.Double ls = new Line2D.Double(CIRCLE_X, CIRCLE_Y, CIRCLE_X + Math.cos((arcSec - 90) * Math.PI / 180) * SEC_LENGTH, CIRCLE_Y + Math.sin((arcSec - 90) * Math.PI / 180) * SEC_LENGTH);
g.setStroke(new BasicStroke(1));
// 秒針的顏色隨機,使動畫效果明顯。
g.setColor(new Color((int) (Math.random() * 255), (int) (Math.random() * 255), (int) (Math.random() * 255)));
g.draw(ls);
/** **** 畫時鍾背景,並將其透明處理 ******* */
// 所有使用bufferedImageGraphics畫的圖像,都畫在bufferedImage上,
// drawImage方法的參數含義分別是:背景圖片對象、目標位置第一個角的X、Y坐標、目標位置第二個角的X、Y坐標、
// 源位置第一個角的X、Y坐標、源位置第二個角的X、Y坐標、圖像對象改變時的通知接受者
bufferedImageGraphics.drawImage(img[imageID], 0, 0, imageSize, imageSize, 0, 0, imageSize + 10, imageSize + 10, this);
// 將背景圖片透明化
for (int j = 0; j < imageSize; j++)
{
for (int i = 0; i < imageSize; i++)
{
// 獲得背景圖像中(i, j)坐標的顏色值
int pix = bufferedImage.getRGB(i, j);
Color c = new Color(pix);
int r = c.getRed();
int gr = c.getGreen();
int b = c.getBlue();
// 通過Color對象的alpha,使顏色透明。
int newpix = new Color(r, gr, b, alpha).getRGB();
// 重新設置背景圖像該象素點的顏色
bufferedImage.setRGB(i, j, newpix);
}
}
/** 以上畫背景操作都是畫在bufferedImage上的,這里要將bufferedImage畫在ClockLabel **/
// 將當前用戶剪貼區 Clip 與橢圓區域相交,並將 Clip 設置為所得的交集
g.clip(new Ellipse2D.Double(95, 45, imageSize, imageSize));
g.setColor(Color.white);
// 在用戶剪貼區畫bufferedImage
g.drawImage(bufferedImage, 95, 45, this);
}
public void run()
{
try
{
//
while (clockThread != null)
{
// 獲得完整的日期格式
DateFormat df = DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.FULL);
// 格式化當前時間
String s = df.format(new Date());
timeLabel.setText(s);
// 每動一次對時鍾指針的角度進行調整
arcSec += 6;
arcMin += 0.1;
arcHour += 0.1 / 60;
// 當角度滿一圈時,歸0
if (arcSec >= 360)
{
arcSec = 0;
}
if (arcMin >= 360)
{
arcMin = 0;
}
if (arcHour >= 360)
{
arcHour = 0;
}
// 實現背景透明度漸變
// 從淺入深,再由深入淺。
if (flag)
{
alpha += 10;
if (alpha == 50)
{
flag = !flag;
}
}
else
{
alpha -= 10;
if (alpha == 0)
{
flag = !flag;
// 當透明度變化一個輪回時,換一張背景圖
imageID++;
if (imageID == 4)
{
imageID = 0;
}
}
}
// 重畫時鍾標簽
repaint();
// 等待1秒鍾
Thread.sleep(1000);
}
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
}
B. 線程的調度分為幾種模型,在java中是使用哪種調度模型
Java程序屬於搶占式調度,哪個線程的優先順序高,哪個線程搶到的CPU時間片的概率就高;如果兩個線程同一個優先順序,則CPU隨機選擇一個執行。
C. java萬年歷
別人寫的 借花獻佛
萬年歷的 但是不實現農歷
如何計算某一天是星期幾?
slowtiger 發表於 2005-10-11 21:43:00
如何計算某一天是星期幾?
—— 蔡勒(Zeller)公式
歷史上的某一天是星期幾?未來的某一天是星期幾?關於這個問題,有很多計算公式(兩個通用計算公式和一些分段計算公式),其中最著名的是蔡勒(Zeller)公式。
即w=y+[y/4]+[c/4]-2c+[26(m+1)/10]+d-1
公式中的符號含義如下,w:星期;c:世紀-1;y:年(兩位數);m:月(m大於等於3,小於等於14,即在蔡勒公式中,某年的1、2月要看作上一年的13、14月來計算,比如2003年1月1日要看作2002年的13月1日來計算);d:日;[ ]代表取整,即只要整數部分。(C是世紀數減一,y是年份後兩位,M是月份,d是日數。
1月和2月要按上一年的13月和 14月來算,這時C和y均按上一年取值。)
算出來的W除以7,余數是幾就是星期幾。如果余數是0,則為星期日。
以2049年10月1日(100周年國慶)為例,用蔡勒(Zeller)公式進行計算,過程如下:
蔡勒(Zeller)公式:w=y+[y/4]+[c/4]-2c+[26(m+1)/10]+d-1
=49+[49/4]+[20/4]-2×20+[26× (10+1)/10]+1-1
=49+[12.25]+5-40+[28.6]
=49+12+5-40+28
=54 (除以7餘5)
即2049年10月1日(100周年國慶)是星期5。
你的生日(出生時、今年、明年)是星期幾?不妨試一試。
不過,以上公式只適合於1582年10月15日之後的情形(當時的羅馬教皇將愷撒大帝制訂的儒略歷修改成格里歷,即今天使用的公歷)。
過程的推導:(對推理不感興趣的可略過不看)
星期制度是一種有古老傳統的制度。據說因為《聖經·創世紀》中規定上帝用了六
天時間創世紀,第七天休息,所以人們也就以七天為一個周期來安排自己的工作和生
活,而星期日是休息日。從實際的角度來講,以七天為一個周期,長短也比較合適。所
以盡管中國的傳統工作周期是十天(比如王勃《滕王閣序》中說的「十旬休暇」,即是
指官員的工作每十日為一個周期,第十日休假),但後來也採取了西方的星期制度。
在日常生活中,我們常常遇到要知道某一天是星期幾的問題。有時候,我們還想知
道歷史上某一天是星期幾。通常,解決這個方法的有效辦法是看日歷,但是我們總不會
隨時隨身帶著日歷,更不可能隨時隨身帶著幾千年的萬年歷。假如是想在計算機編程中
計算某一天是星期幾,預先把一本萬年歷存進去就更不現實了。這時候是不是有辦法通
過什麼公式,從年月日推出這一天是星期幾呢?
答案是肯定的。其實我們也常常在這樣做。我們先舉一個簡單的例子。比如,知道
了2004年5月1日是星期六,那麼2004年5月31日「世界無煙日」是星期幾就不難推算出
來。我們可以掰著指頭從1日數到31日,同時數星期,最後可以數出5月31日是星期一。
其實運用數學計算,可以不用掰指頭。我們知道星期是七天一輪回的,所以5月1日是星
期六,七天之後的5月8日也是星期六。在日期上,8-1=7,正是7的倍數。同樣,5月15
日、5月22日和5月29日也是星期六,它們的日期和5月1日的差值分別是14、21和28,也
都是7的倍數。那麼5月31日呢?31-1=30,雖然不是7的倍數,但是31除以7,余數為2,
這就是說,5月31日的星期,是在5月1日的星期之後兩天。星期六之後兩天正是星期一。
這個簡單的計算告訴我們計算星期的一個基本思路:首先,先要知道在想算的日子
之前的一個確定的日子是星期幾,拿這一天做為推算的標准,也就是相當於一個計算的
「原點」。其次,知道想算的日子和這個確定的日子之間相差多少天,用7除這個日期
的差值,余數就表示想算的日子的星期在確定的日子的星期之後多少天。如果余數是
0,就表示這兩天的星期相同。顯然,如果把這個作為「原點」的日子選為星期日,那
么余數正好就等於星期幾,這樣計算就更方便了。
但是直接計算兩天之間的天數,還是不免繁瑣。比如1982年7月29日和2004年5月
1日之間相隔7947天,就不是一下子能算出來的。它包括三段時間:一,1982年7月29
日以後這一年的剩餘天數;二,1983-2003這二十一個整年的全部天數;三,從2004年
元旦到5月1日經過的天數。第二段比較好算,它等於21*365+5=7670天,之所以要加
5,是因為這段時間內有5個閏年。第一段和第三段就比較麻煩了,比如第三段,需要把
5月之前的四個月的天數累加起來,再加上日期值,即31+29+31+30+1=122天。同理,第
一段需要把7月之後的五個月的天數累加起來,再加上7月剩下的天數,一共是155天。
所以總共的相隔天數是122+7670+155=7947天。
仔細想想,如果把「原點」日子的日期選為12月31日,那麼第一段時間也就是一個
整年,這樣一來,第一段時間和第二段時間就可以合並計算,整年的總數正好相當於兩
個日子的年份差值減一。如果進一步把「原點」日子選為公元前1年12月31日(或者天文
學家所使用的公元0年12月31日),這個整年的總數就正好是想算的日子的年份減一。這
樣簡化之後,就只須計算兩段時間:一,這么多整年的總天數;二,想算的日子是這一
年的第幾天。巧的是,按照公歷的年月設置,這樣反推回去,公元前1年12月31日正好是
星期日,也就是說,這樣算出來的總天數除以7的余數正好是星期幾。那麼現在的問題就
只有一個:這么多整年裡面有多少閏年。這就需要了解公歷的置閏規則了。
我們知道,公歷的平年是365天,閏年是366天。置閏的方法是能被4整除的年份在
2月加一天,但能被100整除的不閏,能被400整除的又閏。因此,像1600、2000、2400
年都是閏年,而1700、1800、1900、2100年都是平年。公元前1年,按公歷也是閏年。
因此,對於從公元前1年(或公元0年)12月31日到某一日子的年份Y之間的所有整年
中的閏年數,就等於
[(Y-1)/4] - [(Y-1)/100] + [(Y-1)/400],
[...]表示只取整數部分。第一項表示需要加上被4整除的年份數,第二項表示需要去掉
被100整除的年份數,第三項表示需要再加上被400整除的年份數。之所以Y要減一,這
樣,我們就得到了第一個計算某一天是星期幾的公式:
W = (Y-1)*365 + [(Y-1)/4] - [(Y-1)/100] + [(Y-1)/400] + D. (1)
其中D是這個日子在這一年中的累積天數。算出來的W就是公元前1年(或公元0年)12月
31日到這一天之間的間隔日數。把W用7除,余數是幾,這一天就是星期幾。比如我們來
算2004年5月1日:
W = (2004-1)*365 + [(2004-1)/4] - [(2004-1)/100] + [(2004-1)/400] +
(31+29+31+30+1)
= 731702,
731702 / 7 = 104528……6,余數為六,說明這一天是星期六。這和事實是符合的。
上面的公式(1)雖然很准確,但是計算出來的數字太大了,使用起來很不方便。仔
細想想,其實這個間隔天數W的用數僅僅是為了得到它除以7之後的余數。這啟發我們是
不是可以簡化這個W值,只要找一個和它余數相同的較小的數來代替,用數論上的術語
來說,就是找一個和它同餘的較小的正整數,照樣可以計算出准確的星期數。
顯然,W這么大的原因是因為公式中的第一項(Y-1)*365太大了。其實,
(Y-1)*365 = (Y-1) * (364+1)
= (Y-1) * (7*52+1)
= 52 * (Y-1) * 7 + (Y-1),
這個結果的第一項是一個7的倍數,除以7餘數為0,因此(Y-1)*365除以7的余數其實就
等於Y-1除以7的余數。這個關系可以表示為:
(Y-1)*365 ≡ Y-1 (mod 7).
其中,≡是數論中表示同餘的符號,mod 7的意思是指在用7作模數(也就是除數)的情
況下≡號兩邊的數是同餘的。因此,完全可以用(Y-1)代替(Y-1)*365,這樣我們就得到
了那個著名的、也是最常見到的計算星期幾的公式:
W = (Y-1) + [(Y-1)/4] - [(Y-1)/100] + [(Y-1)/400] + D. (2)
這個公式雖然好用多了,但還不是最好用的公式,因為累積天數D的計算也比較麻
煩。是不是可以用月份數和日期直接計算呢?答案也是肯定的。我們不妨來觀察一下各
個月的日數,列表如下:
月 份:1月 2月 3月 4月 5月 6月 7月 8月 9月 10月 11月 12月
--------------------------------------------------------------------------
天 數: 31 28(29) 31 30 31 30 31 31 30 31 30 31
如果把這個天數都減去28(=4*7),不影響W除以7的余數值。這樣我們就得到另一張
表:
月 份:1月 2月 3月 4月 5月 6月 7月 8月 9月 10月 11月 12月
------------------------------------------------------------------------
剩餘天數: 3 0(1) 3 2 3 2 3 3 2 3 2 3
平年累積: 3 3 6 8 11 13 16 19 21 24 26 29
閏年累積: 3 4 7 9 12 14 17 20 22 25 27 30
仔細觀察的話,我們會發現除去1月和2月,3月到7月這五個月的剩餘天數值是3,2,3,2,
3;8月到12月這五個月的天數值也是3,2,3,2,3,正好是一個重復。相應的累積天數中,
後一月的累積天數和前一月的累積天數之差減去28就是這個重復。正是因為這種規律的
存在,平年和閏年的累積天數可以用數學公式很方便地表達:
╭ d; (當M=1)
D = { 31 + d; (當M=2) (3)
╰ [ 13 * (M+1) / 5 ] - 7 + (M-1) * 28 + d + i. (當M≥3)
其中[...]仍表示只取整數部分;M和d分別是想算的日子的月份和日數;平年i=0,閏年
i=1。對於M≥3的表達式需要說明一下:[13*(M+1)/5]-7算出來的就是上面第二個表中的
平年累積值,再加上(M-1)*28就是想算的日子的月份之前的所有月份的總天數。這是一
個很巧妙的辦法,利用取整運算來實現3,2,3,2,3的循環。比如,對2004年5月1日,有:
D = [ 13 * (5+1) / 5 ] - 7 + (5-1) * 28 + 1 + 1
= 122,
這正是5月1日在2004年的累積天數。
假如,我們再變通一下,把1月和2月當成是上一年的「13月」和「14月」,不僅仍
然符合這個公式,而且因為這樣一來,閏日成了上一「年」(一共有14個月)的最後一
天,成了d的一部分,於是平閏年的影響也去掉了,公式就簡化成:
D = [ 13 * (M+1) / 5 ] - 7 + (M-1) * 28 + d. (3≤M≤14) (4)
上面計算星期幾的公式,也就可以進一步簡化成:
W = (Y-1) + [(Y-1)/4] - [(Y-1)/100] + [(Y-1)/400] + [ 13 * (M+1) / 5 ] - 7
+ (M-1) * 28 + d.
因為其中的-7和(M-1)*28兩項都可以被7整除,所以去掉這兩項,W除以7的余數不變,
公式變成:
W = (Y-1) + [(Y-1)/4] - [(Y-1)/100] + [(Y-1)/400] + [ 13 * (M+1) / 5 ] + d.
(5)
當然,要注意1月和2月已經被當成了上一年的13月和14月,因此在計算1月和2月的日子
的星期時,除了M要按13或14算,年份Y也要減一。比如,2004年1月1日是星期四,用這
個公式來算,有:
W = (2003-1) + [(2003-1)/4] - [(2003-1)/100] + [(2003-1)/400] + [13*(13+1)/5]
+ 1
= 2002 + 500 - 20 + 5 + 36 + 1
= 2524;
2524 / 7 = 360……4.這和實際是一致的。
公式(5)已經是從年、月、日來算星期幾的公式了,但它還不是最簡練的,對於年
份的處理還有改進的方法。我們先來用這個公式算出每個世紀第一年3月1日的星期,列
表如下:
年份: 1(401,801,…,2001) 101(501,901,…,2101)
--------------------------------------------------------------------
星期: 4 2
====================================================================
年份:201(601,1001,…,2201) 301(701,1101,…,2301)
--------------------------------------------------------------------
星期: 0 5
可以看出,每隔四個世紀,這個星期就重復一次。假如我們把301(701,1101,…,2301)
年3月1日的星期數看成是-2(按數論中對余數的定義,-2和5除以7的余數相同,所以可
以做這樣的變換),那麼這個重復序列正好就是一個4,2,0,-2的等差數列。據此,我們
可以得到下面的計算每個世紀第一年3月1日的星期的公式:
W = (4 - C mod 4) * 2 - 4. (6)
式中,C是該世紀的世紀數減一,mod表示取模運算,即求余數。比如,對於2001年3月
1日,C=20,則:
W = (4 - 20 mod 4) * 2 - 4
= 8 - 4
= 4.
把公式(6)代入公式(5),經過變換,可得:
(Y-1) + [(Y-1)/4] - [(Y-1)/100] + [(Y-1)/400] ≡ (4 - C mod 4) * 2 - 1
(mod 7). (7)
因此,公式(5)中的(Y-1) + [(Y-1)/4] - [(Y-1)/100] + [(Y-1)/400]這四項,在計算
每個世紀第一年的日期的星期時,可以用(4 - C mod 4) * 2 - 1來代替。這個公式寫
出來就是:
W = (4 - C mod 4) * 2 - 1 + [13 * (M+1) / 5] + d. (8)
有了計算每個世紀第一年的日期星期的公式,計算這個世紀其他各年的日期星期的公式
就很容易得到了。因為在一個世紀里,末尾為00的年份是最後一年,因此就用不著再考
慮「一百年不閏,四百年又閏」的規則,只須考慮「四年一閏」的規則。仿照由公式(1)
簡化為公式(2)的方法,我們很容易就可以從式(8)得到一個比公式(5)更簡單的計算任意
一天是星期幾的公式:
W = (4 - C mod 4) * 2 - 1 + (y-1) + [y/4] + [13 * (M+1) / 5] + d. (9)
式中,y是年份的後兩位數字。
如果再考慮到取模運算不是四則運算,我們還可以把(4 - C mod 4) * 2進一步改寫
成只含四則運算的表達式。因為世紀數減一C除以4的商數q和余數r之間有如下關系:
4q + r = C,
其中r即是 C mod 4,因此,有:
r = C - 4q
= C - 4 * [C/4]. (10)
則
(4 - C mod 4) * 2 = (4 - C + 4 * [C/4]) * 2
= 8 - 2C + 8 * [C/4]
≡ [C/4] - 2C + 1 (mod 7). (11)
把式(11)代入(9),得到:
W = [C/4] - 2C + y + [y/4] + [13 * (M+1) / 5] + d - 1. (12)
這個公式由世紀數減一、年份末兩位、月份和日數即可算出W,再除以7,得到的余數是
幾就表示這一天是星期幾,唯一需要變通的是要把1月和2月當成上一年的13月和14月,
C和y都按上一年的年份取值。因此,人們普遍認為這是計算任意一天是星期幾的最好的
公式。這個公式最早是由德國數學家克里斯蒂安·蔡勒(Christian Zeller, 1822-
1899)在1886年推導出的,因此通稱為蔡勒公式(Zeller』s Formula)。為方便口算,
式中的[13 * (M+1) / 5]也往往寫成[26 * (M+1) / 10]。
現在仍然讓我們來算2004年5月1日的星期,顯然C=20,y=4,M=5,d=1,代入蔡勒
公式,有:
W = [20/4] - 40 + 4 + 1 + [13 * (5+1) / 5] + 1 - 1
= -15.
注意負數不能按習慣的余數的概念求余數,只能按數論中的余數的定義求余。為了方便
計算,我們可以給它加上一個7的整數倍,使它變為一個正數,比如加上70,得到55。
再除以7,餘6,說明這一天是星期六。這和實際是一致的,也和公式(2)計算所得的結
果一致。
最後需要說明的是,上面的公式都是基於公歷(格里高利歷)的置閏規則來考慮
的。對於儒略歷,蔡勒也推出了相應的公式是:
W = 5 - C + y + [y/4] + [13 * (M+1) / 5] + d - 1. (13)
========================================
(2005-10-20 22:25:00) --------(4575252)
計算任何一天是星期幾的幾種演算法
近日在論壇上看到有人在問星期演算法,特別整理了一下,這些演算法都是從網上搜索而來,演算法的實現是我在項目中寫的。希望對大家有所幫助。
一:常用公式
W = [Y-1] + [(Y-1)/4] - [(Y-1)/100] + [(Y-1)/400] + D
Y是年份數,D是這一天在這一年中的累積天數,也就是這一天在這一年中是第幾天。
二:蔡勒(Zeller)公式
w=y+[y/4]+[c/4]-2c+[26(m+1)/10]+d-1
公式中的符號含義如下,w:星期;c:世紀;y:年(兩位數); m:月(m大於等於3,小於等於14,即在蔡勒公式中,某年的1、2月要看作上一年的13、14月來計算,比如2003年1月1日要看作2002年的13月1日來計算);d:日;[ ]代表取整,即只要整數部分。
相比於通用通用計算公式而言,蔡勒(Zeller)公式大大降低了計算的復雜度。
三:對蔡勒(Zeller)公式的改進
作者:馮思琮
相比於另外一個通用通用計算公式而言,蔡勒(Zeller)公式大大降低了計算的復雜度。不過,筆者給出的通用計算公式似乎更加簡潔(包括運算過程)。現將公式列於其下:
W=[y/4]+r (y/7)-2r(c/4)+m』+d
公式中的符號含義如下,r ( )代表取余,即只要余數部分;m』是m的修正數,現給出1至12月的修正數1』至12』如下:(1』,10』)=6;(2』,3』,11』)=2;(4』,7』)=5;5』=0;6』=3;8』=1;(9』,12』)=4(注意:在筆者給出的公式中,y為潤年時1』=5;2』=1)。其他符號與蔡勒(Zeller)公式中的含義相同。
四:基姆拉爾森計算公式
這個公式名稱是我給命名的,哈哈希望大家不要見怪。
W= (d+2*m+3*(m+1)/5+y+y/4-y/100+y/400) mod 7
在公式中d表示日期中的日數,m表示月份數,y表示年數。
注意:在公式中有個與其他公式不同的地方:
把一月和二月看成是上一年的十三月和十四月,例:如果是2004-1-10則換算成:2003-13-10來代入公式計算。
D. 求解答java Time schele()方法真能實現線程的精確延時觸發嗎
如果是要保證的准確性還是考慮一下spring的quartz(所謂的spring定時器org.springframework.scheling.quartz.*工具包)或真直接調用第三方式具類org.quartz。至少你說的那個jdk自帶的java.util.* 工具包實現。這兩個實現原理都應該是依賴jvm環境與系統時間,但是你所說的按時間片這個概念還真沒接觸過。本人對這個所謂有的中央控制器對時間咋控的還真不了妥。不過我們是做軟體的,只要壓力測試都能保證輪詢時間與系統是一致的,還要考慮那麼遠么?
E. 關於java定時器Timer的疑問,當執行時間大於間隔時間會怎樣
這個問題,我正好以前研究過
先回答你的問題:是等待第一次任務執行完
然後細說:
如果你用的是 java.util.Timer,那麼這個Timer 有兩種執行模式:
fixed-rate:對應於 scheleAtFixedRate 方法
fixed-delay:對應於 schele 方法
fixed-rate 模式,如果某次任務超時了,它會智能的縮短下一次任務的執行時間,
比如你 10 秒一次任務,但是任務執行了 15 秒,
那麼下一次任務如果沒有超時的話,可能也不會分配給他完整的 10 秒,而是 5 秒
目的是盡量讓後面的任務可以趕上來
任務執行時間可能是這樣一個模式:
第一次任務:15 秒(超時)
第二次任務:8 秒(未超時)
第三次任務:7 秒(未超時)
第四次任務:10 秒(未超時)
……
如果你任務一直都在超時,可能每次任務執行完之後,就會立刻執行下一次任務
其實如果你每次任務都超時,你應該考慮的是,
在程序中,就增加任務的延時
fixed-delay 模式,如果你某次任務超時了,也不會影響下一次任務的執行時間
10 秒周期的任務,執行了 15 秒
那下一次任務依然是分配完整的 10 秒
任務執行時間可能是這樣一個模式:
第一次任務:15 秒(超時)
第二次任務:10 秒(未超時)
第三次任務:10 秒(未超時)
第四次任務:15 秒(超時)
如果你用的是 javax.swing.Timer,這個是按固定周期觸發的
由於這個 Timer 是在 EDT (Java GUI 的事件指派線程)上執行的
EDT 有智能合並任務的處理
如果你某次任務超時了,那麼下一次任務將和下下次任務智能合並成一個任務
比如 10 秒周期的任務,執行了 15 秒
任務執行時間可能是這樣一個模式:
第一次任務:20 秒(超時)
第二次任務:合並
第三次任務:10 秒(未超時)
……
其實 javax.swing.Timer 也有 Fixed Rate 模式,
可以通過 setCoalesce(false); 方法開啟
但是不建議這樣使用
F. Java優勢有哪些
JAVA的優勢是什麼?
Java語言是目前的排行第一的語言,其優勢也是非常突出的,那麼具體體現在哪裡吶?
我來給你說一下Java的優勢:
1.java的風格類似C++但不同於C++,從某種意義上講,java是C++的一個變種;
2.java摒棄了C、C++中的容易引發錯誤和難以理解的指針,結構,以及內存管理等;
3.java提供了豐富的類庫,很方便開發程序;
4.java是完全面向對象的語言,支持 繼承,重載,多態等面向對象的特性;
5.C++是面向對象和面向過程的混合語言, C是純面向過程的語言;
6.java是一種強類型的語言,比C/C++檢查還要嚴格,java區分大小寫字母;
7.java提供了自動垃圾回收機制gc,有效避免了C、C++的內存泄漏問題;
8.java禁止非法訪問內存,因為java沒有指針,內存的申請和釋放;
9.跨平台,java的源代碼編譯成結構中立的中間格式,這種格式與機器無關,只要在安裝有JVM(java虛擬機)的電腦上,都能運行這種與機器無關的中間文件;java一次編譯,到處運行;
10.對於不同的操作系統,會有不同的java虛擬機,這也就決定了java的可移植性;
11.java支持多線程,簡單理解,如果是單核CPU,那麼會通過時間片輪轉的方式,多線程執行程序,如果是多核CPU,那麼就可以理解為,兩個或多個線程同時運行。
一、做網站
Java可以用來做網站,很多大型網站都是用Java寫的,比如我們熟悉的B站,所以想學習Java的同學還可以負責網站方面的製作,這方面的崗位(網站開發)也比較多,一直以來都相當流行。
二、做安卓軟體
安卓是基於Linux的操作系統,其中源代碼就是Java,市面上所有的安卓手機都是修改Java運行的,對於更多的開發人員來說,他們更多的時間是花在開發APP上面。你隨便打開一個App應用,他們就是用Java語言做的。
三、做游戲
電腦上的大多數游戲也是用Java來開發的,最經典的就是《我的世界》,還有當今世界最具影響力的游戲英雄聯盟,吃雞也是用Java寫的
四、寫軟體
很多編程語言都是可以來寫軟體的,但Java是現在應用最廣泛的,比如:企業級應用開發,還有OA、郵箱、物流、醫療、投票、金融、考試、礦山等信息方面的系統,Java都佔有極為重要的地位。現在國內的最熱門的就是手機應用,學習Java去做手機應用還是比較吃香的。
說啦這么多那麼應該如何學習Java那?
在這個里我使用腦圖給大家出一個,Java的學習線路供大家參考:
Java在市場中的佔有率是非常之高的,希望大都成為優秀的程序員!!!
G. java怎麼寫有一個功能,就是它會設置一個時間段,然後到了這個結束時間的時候會修改它的狀態
創建個線程監聽時間,到時間就修改狀態。
H. JAVA 多個線程運行時間問題
我理解,准確的說你這個都不是多線程執行,即使是多線程執行,也有一定的時間差,cpu還要輪片。
符合你要求的寫法,你可以試試,實現了Runnable下面的run();在run里休眠
@Override
publicvoidrun(){
try{
Thread.sleep(5000);
System.out.println("done");
}catch(InterruptedExceptione){
e.printStackTrace();
}
}
而你後面的m1.mySleep()和m2.mySleep()相當於在main的主線程中阻塞執行,目測需要10秒多才能看到兩個done!!