Java(四)对象与类

1、系统已有类

     所有的 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();//结束时,若不是星期起始日则换行
	}
}

         运行结果:


2、用户自定义类

      要想创建一个完整的程序,应该将若干类组合在一起,其中只有一个类有 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;
}

                  注意,java 与 C++ 不同,java中,所有的方法都必须在类的内部定义,但并不表示他们是内联方法。

        

         (3) getName方法、getSalary方法和 getHireDay 方法,都是典型的访问器方法,由于它们只返回实例域值,也称为域访问器

         (4)final 实例域,构建对象时必须初始化这样的域,初始化之后不能进行修改。

3、静态域与静态方法

      关键字 static 被解释为:属于类且不属于对象的变量和函数。

     (1)静态域

              如果将域定义为 static,每个类中只有一个这样的域。每一个对象对于所有的实例域都有自己的拷贝,而却都共享一个静态域。

     (2)静态常量

              静态变量用的少,但静态常量却使用的比较多。跟静态域功能差不多。

     (3)静态方法

              静态方法是一种不能向对象实施操作的方法,所以不能在静态方法中访问实例域。但是静态方法可以访问自身类中的静态域。

             可以认为静态方法是没有 this 参数的方法。可以使用类名来调用静态方法

             在下面两种情况下使用静态方法:

                    a、一个方法不需要访问对象状态,其所参数都是通过显示参数提供

                           如: Math.pow(x,a) 计算 x 的 a 次幂,不使用任何 Math 对象。

                    b、一个方法只需要访问类的静态域

                           如:

public static int getNextId()
{
     return nextId; //返回的是一个静态域
}

                               可以通过类名调用这个方法: int n = Employee.getNextId();

4、方法参数

     在 java 程序设计语言中,方法参数的使用情况:

                               a、一个方法不能修改一个基本数据类型的参数(即数值型和布尔型)

                               b、一个方法可以改变一个对象参数的状态

                               c、一个方法不能实现让对象参数引用一个新的对象

5、对象构造

     (1)重载

              如果多个方法有相同的名字、不同的参数,便产生了重载。

              要完整的描述一个方法,需要指出方法名以及参数类型。这叫做方法的签名。返回类型不是签名的一部分。

     (2)默认域初始化

              如果在构造器中没有显示地给域赋初值,那么就会被自动赋为默认值:数值为0,布尔值为 false,对象引用为 null。

     (3)默认构造器

              默认构造器是指没有参数的构造器。

              如果一个类没有编写构造器,系统会提供一个默认构造器,这个默认构造器将所有的实例域设置为默认值

     (4)调用另一个构造器

              关键字 this 引用方法的隐式参数,然后,还有另一个含义:如果构造器的第一个语句形如 this(...),这个构造器

                               将调用同一个类的另一个构造器,如:

public Employee(double s)
{
      //调用 Employee(String,double)
      this("Employee #" + nextId, s);
      nextId++;
}

      (5)初始化块

               初始化数据域的方法有三种:在构造器中设置值,在声明中赋值,还有就是称为初始化块。

               在一个类的声明中可以包含多个代码块,只要构造类的对象,这些块就会被执行。例如:

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++;
	}
}

运行结果:


       (6)对象析构与 finalize 方法

                java 有自动的垃圾回收器,不需要人工回收内存,所以不支持析构器。

                当然,如果某些对象使用了内存之外的其他资源,例如,文件或使用了系统资源的另一个对象的句柄,这种情况下,需要回收。

                           可以为任何一个类添加 finalize 方法,这个方法将在垃圾回收器清除对象之前调用。

6、包

      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)

7、类设计技巧

     (1)一定将数据设计为私有

     (2)一定要对数据初始化

     (3)不要在类中使用过多的基本数据类型

     (4)不是所有的域都需要独立的域访问器和域更改器

     (5)使用标准格式进行类的定义

               一定采用下面的顺序书写类的内容:

                       公有访问特性部分

                       包作用域访问特性部分

                       私有访问特性部分

               在每一部分中,应该按照下列顺序列出:

                       实例方法

                       静态方法

                       实例域

                       静态域

     (6)将职责过多的类进行分解

     (7)类名和方法名要能够体现它们的职责

             

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值