所有的 java 对象都存储在堆中。
java 类 实例:
import java.text.DateFormatSymbols;
import java.util.*;
public class CalendarTest
{
public static void main(String[] args)
{
//构造一个日历对象,并用当前日期和时间进行初始化
GregorianCalendar d = new GregorianCalendar();
int today = d.get(Calendar.DAY_OF_MONTH);//获取当时的日
int month = d.get(Calendar.MONTH);//获取当时的月
int year = d.get(Calendar.YEAR);//获取当时的年
int todayWeek = d.get(Calendar.DAY_OF_WEEK);//获取当时星期几
d.set(Calendar.DAY_OF_MONTH,1);//将d设置为这个月的第一天
int weekday = d.get(Calendar.DAY_OF_WEEK);//获取这一天是星期几
String[] weekdayNames = new DateFormatSymbols().getShortWeekdays();//转换星期几的格式
//输出当前的年月日星期,因为月从0为基准,所以这里要加1
System.out.println();
System.out.println("Today is : "+year+"年"+(month+1)+"月"+today+"日"+weekdayNames[todayWeek]);
System.out.println();
int firstDayOfWeek = d.getFirstDayOfWeek();//获取当前地区星期的起始日
int indent = 0;
while (weekday != firstDayOfWeek)//如果weekday不是星期的起始日,将日期后退,
{ //直接是起始日为止,计算离当前天的距离
indent++;
d.add(Calendar.DAY_OF_MONTH, -1);
weekday = d.get(Calendar.DAY_OF_WEEK);
}
do //依次输出星期的缩写字符串
{
System.out.printf("%4s", weekdayNames[weekday]);//第一次循环时weekday是星期的起始日
d.add(Calendar.DAY_OF_MONTH, 1);
weekday = d.get(Calendar.DAY_OF_WEEK);
}
while (weekday != firstDayOfWeek);//当不是星期的起始日时继续循环
System.out.println();
for (int i = 1; i <= indent; i++)//由开头进行缩进上面求出的距离
System.out.print(" ");//每次输出5个空格
d.set(Calendar.DAY_OF_MONTH, 1);//设置 d 为这个月的第一天
do
{
int day = d.get(Calendar.DAY_OF_MONTH);
System.out.printf("%4d", day);//输出日
if (day == today) System.out.print("*");//若是今天,则后面加上 *,不是则加空格
else System.out.print(" ");
d.add(Calendar.DAY_OF_MONTH,1);//将d设置为下一天
weekday = d.get(Calendar.DAY_OF_WEEK);//获取星期
if (weekday == firstDayOfWeek) System.out.println();//如果是每个星期的第一天则换行输出
}
while (d.get(Calendar.MONTH) == month);//如果d还是在当前的月就继续循环
if (weekday != firstDayOfWeek) System.out.println();//结束时,若不是星期起始日则换行
}
}
要想创建一个完整的程序,应该将若干类组合在一起,其中只有一个类有 main 方法。
编写类所采用的风格是类的方法在前面,域在后面。
文件名必须与 public 类的名字相匹配,在一个源文件中,只能有一个公有类,但是可以有任意数目的非公有类
实例:
import java.util.*;
public class EmployeeTest
{
public static void main(String[] agrs)
{
Employee[] staff = new Employee[3];
staff[0] = new Employee("Ai",2000,1992,1,12);
staff[1] = new Employee("Bi",3000,1993,8,29);
staff[2] = new Employee("Ci",2500,1992,11,5);
for (Employee e : staff)
e.raiseSalary(5);
for (Employee e : staff)
System.out.println("name="+e.getName()+",salary="
+e.getSalary()+",hireDay="+e.getHireDay());
}
}
class Employee
{
public Employee(String n, double s, int year, int month, int day) //构造器
{
name = n;
salary = s;
GregorianCalendar calendar=new GregorianCalendar(year,month-1,day);
hireDay = calendar.getTime();
}
public String getName()
{
return name;
}
public double getSalary()
{
return salary;
}
public Date getHireDay()
{
return hireDay;
}
public void raiseSalary(double byPercent)
{
double raise = salary * byPercent / 100;
salary += raise;
}
//实例域
private String name;
private double salary;
private Date hireDay;
}
Employee 类,包含一个构造器和4个方法,还有三个实例域用来存放将要操作的数据。
(1)构造器:与类名相同,在构造 Employee 类的对象时,构造器被运行,以便将实例域初始化为所希望的状态。
每一个类有一个以上的构造器,构造器可以有任意个参数,没有返回值,总是伴随着 new 操作一起调用(C++程序员容易忘记这点)
(2)隐式参数与现实参数:
public void raiseSalary(double byPercent)
{
double raise = salary * byPercent / 100;
salary += raise;
}
raiseSalary 方法有两个参数,第一个参数时方法名前的 Employee 类对象,称为隐式参数,没有出现在方法声明中
第二个参数是在方法名后面括号里的 double byPercent ,称为显式参数。
在每一个方法中,关键字 this 表示隐式参数。如果需要的话,可以用下列方式编写 raiseSalary 方法:
public void raiseSalary(double byPercent)
{
double raise = this.salary * byPercent / 100;
this.salary += raise;
}
(3) getName方法、getSalary方法和 getHireDay 方法,都是典型的访问器方法,由于它们只返回实例域值,也称为域访问器
(4)final 实例域,构建对象时必须初始化这样的域,初始化之后不能进行修改。
关键字 static 被解释为:属于类且不属于对象的变量和函数。
如果将域定义为 static,每个类中只有一个这样的域。每一个对象对于所有的实例域都有自己的拷贝,而却都共享一个静态域。
静态变量用的少,但静态常量却使用的比较多。跟静态域功能差不多。
静态方法是一种不能向对象实施操作的方法,所以不能在静态方法中访问实例域。但是静态方法可以访问自身类中的静态域。
可以认为静态方法是没有 this 参数的方法。可以使用类名来调用静态方法
在下面两种情况下使用静态方法:
a、一个方法不需要访问对象状态,其所参数都是通过显示参数提供
如: Math.pow(x,a) 计算 x 的 a 次幂,不使用任何 Math 对象。
b、一个方法只需要访问类的静态域
如:
public static int getNextId()
{
return nextId; //返回的是一个静态域
}
在 java 程序设计语言中,方法参数的使用情况:
a、一个方法不能修改一个基本数据类型的参数(即数值型和布尔型)
b、一个方法可以改变一个对象参数的状态
c、一个方法不能实现让对象参数引用一个新的对象
如果多个方法有相同的名字、不同的参数,便产生了重载。
要完整的描述一个方法,需要指出方法名以及参数类型。这叫做方法的签名。返回类型不是签名的一部分。
如果在构造器中没有显示地给域赋初值,那么就会被自动赋为默认值:数值为0,布尔值为 false,对象引用为 null。
默认构造器是指没有参数的构造器。
如果一个类没有编写构造器,系统会提供一个默认构造器,这个默认构造器将所有的实例域设置为默认值
关键字 this 引用方法的隐式参数,然后,还有另一个含义:如果构造器的第一个语句形如 this(...),这个构造器
将调用同一个类的另一个构造器,如:
public Employee(double s)
{
//调用 Employee(String,double)
this("Employee #" + nextId, s);
nextId++;
}
初始化数据域的方法有三种:在构造器中设置值,在声明中赋值,还有就是称为初始化块。
在一个类的声明中可以包含多个代码块,只要构造类的对象,这些块就会被执行。例如:
class Employee
{
public Employee(String n, double s)
{
name = n;
salary = s;
}
public Employee()
{
name = "";
salary = 0;
}
...
private static int nextId;
private int id;
private String name;
private double salary;
...
//初始化块
{
id = nextId;
nextId++;
}
}
a、所有数据域被初始化为默认值
b、按照在类声明中出现的次序,依次执行所有域初始化语句和初始化块
c、如果构造器第一行调用了第二个构造器,则执行第二个构造器主体
d、执行这个构造器的主体
如果对类的静态域进行初始化的代码比较复杂,那么可以使用静态的初始化块,将代码放在一个块中,并标记关键字 static。
示例:
static
{
Random generator = new Random();
nextId = generator.nextInt(10000);
}
//功能是将雇员Id的初始值赋予一个小于10000的随机整数
实例:
import java.util.*;
public class ConstructorTest
{
public static void main(String[] agrs)
{
Employee[] staff = new Employee[3];
staff[0] = new Employee("Ai",20000);
staff[1] = new Employee(50000);
staff[2] = new Employee();
for (Employee e : staff)
System.out.println("name="+e.getName()+",id="
+e.getId()+",salary="+e.getSalary());
}
}
class Employee
{
public Employee(String n, double s)
{
name = n;
salary = s;
}
public Employee(double s)
{
//调用 Employee(String,double)构造器
this("Employee #" + nextId, s);
}
public Employee() //默认构造器
{
}
public String getName()
{
return name;
}
public double getSalary()
{
return salary;
}
public int getId()
{
return id;
}
private static int nextId;
private int id;
private String name = ""; //实例域初始化
private double salary;
static //静态初始块
{
Random generator = new Random();//构造一个新的随机数生成器
nextId = generator.nextInt(10000);//返回一个 0到9999之间的随机数
}
//对象初始化块
{
id = nextId;
nextId++;
}
}
运行结果:
java 有自动的垃圾回收器,不需要人工回收内存,所以不支持析构器。
当然,如果某些对象使用了内存之外的其他资源,例如,文件或使用了系统资源的另一个对象的句柄,这种情况下,需要回收。
可以为任何一个类添加 finalize 方法,这个方法将在垃圾回收器清除对象之前调用。
java 允许使用包(package)将类组织起来。
使用包的主要原因是确保类名的唯一性,将同名的类放到不同的包中,就不会产生冲突,为了确保包名的绝对唯一性,Sun
公司建议将公司的因特网域名以逆序的形式作为包名,并且对于不同的项目使用不同的子包。
(1)类的导入
两种方式访问另一个包中的公有类:
a、在每个类名之前添加完整的包名,如:java.util.Date today = new java.util.Date(); ,显然不方便
b、简单常用的方式是使用 import 语句,可以使用 import 语句导入一个特定的类或者整个包。
如: import java.util.*;
注意,只能使用星号(*)导入一个包,而不是所有包。当导入多个包,放生命名冲突时,应明确指出调用哪个包。
(2)静态导入:可在包名前加上 static,就可以使用包类的静态方法和静态域,而不必加类名前缀。
(3)将类放入包中:必须将包名放在源文件开头
package 包名;
类{...}
若没有放置 package 语句,则源文件默认放置在一个默认包(default package)
(1)一定将数据设计为私有
(2)一定要对数据初始化
(3)不要在类中使用过多的基本数据类型
(4)不是所有的域都需要独立的域访问器和域更改器
(5)使用标准格式进行类的定义
一定采用下面的顺序书写类的内容:
公有访问特性部分
包作用域访问特性部分
私有访问特性部分
在每一部分中,应该按照下列顺序列出:
实例方法
静态方法
实例域
静态域
(6)将职责过多的类进行分解
(7)类名和方法名要能够体现它们的职责