用户自定义类
Java中,简单的类定义形式为:
class ClassName
{
Field1
Field2
…
Constructor1
Constructor2
…
Methord1
Methord2
…
}
即习惯上类的定义格式为:
package xxx;
import xxx;
public class Xxx
{
属性;
构造器;
方法;
}
定义属性:实例变量
格式:【修饰符】 类型 变量名 【=?】
- 实例变量定义在类中,但是在·任何方法之外
- 实例变量在未赋初始值的情况下有默认值:数值型=0;布尔型=false;对象型=null
- 实例变量的作用域至少在本类内部,受访问控制符的限制。
- 在重合作用域,实例变量和局部变量命名冲突时,系统默认局部优先。
定义方法
格式:【修饰符】 返回类型 方法名(参数列表)【throws异常】{}
- java中所有参数都是通过值传递
- 无返回值时,返回类型定义为void
- 返回类型与方法名相邻,其他修饰符可调换位置
例如,构造一个简单的employee类
Class Employee
{
Private static int nextId;
Private int id=assignId();
Private static int assignId();
{
Int r=nextId;
nextId++;
return r;
}
例:构造一个employee类,将每个雇员的薪水提升5%,并将每个雇员的信息打印出来
package ClassesAndProjects;
import java.time.*;
import java.util.*;
/*this projecct test the Employee class
* @version 4.2 2019/7/16
* @author son
*/
public class EmployeeTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
Employee[] staff=new Employee[3];//构造一个employee数组,分别填入三个雇员对象
staff[0]=new Employee("小芳",7900, 1988, 10, 1);
staff[1]=new Employee("小花",7800, 1990, 6, 6);
staff[2]=new Employee("小梦",7700, 1990, 9, 1);
//FOR raise salary by 6%,利用employee类中的raiseSalary方法将雇员的薪水提升百分之五
for(Employee e:staff)
e.raiseSalary(5);
for(Employee e:staff)
System.out.println("name="+e.getName()+"salary="+e.getSalary()+"hireDay="+e.getHireDay()+"AfterRiase");//print out information about all Employee objects
}
}
class Employee{
//Employee类的实域中有三个实例域用来存放将要存放的数据,如下:
private String name;
private double salary;
private LocalDate hireDay;
//注意:关键字private只能由Employee类自身的方法能够访问这些实例域,而关键字public则意味着任何类的任何方法都可以调用这些方法。
一般建议将实例域标记为private,这里有两个实例域本身就是对象:name域是String 类对象;hireDay域是LocalDate类对象。这说明,类通常包含类型属于某个类类型的实例域。
//构造器
public Employee(String n, double s,int year,int month,int day)
{
name=n;
salary=s;
hireDay=LocalDate.of(year,month,day);
}
//方法1
public String getName() {
return name;
}
//方法2
public double getSalary() {
return salary;
}
//方法3
public LocalDate getHireDay()
{
return hireDay;
}
//方法4
public void raiseSalary(double byPercent)
{
//Scanner in=new Scanner(System.in);
double raise=salary=salary*byPercent/100;
salary+=raise;
}
}
运行结果:
在一个源文件中,只能有一个公有类,但可以有任意数目的非公有类。这个例子的文件名与public类(这里指public class EmployeeTest)相匹配,程序中将包含main方法的类提供给字节码解析器。
构造器
在构造类对象时,构造器会运行,以便将实例域初始化
格式为:【修饰符】 类名(参数列表){}
在上例中的构造器如下:
package ClassesAndProjects;
import java.time.*;
import java.util.*;
/*this projecct test the Employee class
* @version 4.2 2019/7/16
* @author son
*/
public class EmployeeTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
Employee[] staff=new Employee[3];//构造一个employee数组,分别填入三个雇员对象
staff[0]=new Employee("小芳",7900, 1988, 10, 1);
staff[1]=new Employee("小花",7800, 1990, 6, 6);
staff[2]=new Employee("小梦",7700, 1990, 9, 1);
//FOR raise salary by 6%,利用employee类中的raiseSalary方法将雇员的薪水提升百分之五
for(Employee e:staff)
e.raiseSalary(5);
for(Employee e:staff)
System.out.println("name="+e.getName()+"salary="+e.getSalary()+"hireDay="+e.getHireDay()+"AfterRiase");//print out information about all Employee objects
}
}
class Employee{
//Employee类的实域中有三个实例域用来存放将要存放的数据,如下:
private String name;
private double salary;
private LocalDate hireDay;
//注意:关键字private只能由Employee类自身的方法能够访问这些实例域,而关键字public则意味着任何类的任何方法都可以调用这些方法。
一般建议将实例域标记为private,这里有两个实例域本身就是对象:name域是String 类对象;hireDay域是LocalDate类对象。这说明,类通常包含类型属于某个类类型的实例域。
//构造器
public Employee(String n, double s,int year,int month,int day)
{
name=n;
salary=s;
hireDay=LocalDate.of(year,month,day);
}
//方法1
public String getName() {
return name;
}
//方法2
public double getSalary() {
return salary;
}
//方法3
public LocalDate getHireDay()
{
return hireDay;
}
//方法4
public void raiseSalary(double byPercent)
{
//Scanner in=new Scanner(System.in);
double raise=salary=salary*byPercent/100;
salary+=raise;
}
}
注意:1.构造器与类同名;2.每个类可以有一个以上的构造器;3.构造器可以有0个,1个或者多个参数(不要在构造器中定义与实例域重名的局部变量);4.构造器没有返回值;5.构造器总是伴随着new操作符的执行而被调用(不能对一个已经存在的对象调用构造器来达到重新设置实例域的目的),一个对象生成的周期中构造器只用一次。
类,对象,实例三者的关系:
类是对象的模板,可以实例化为对象;
对象是类的个体;
实例是实现的对象。
Student s=new Student();
//Student为类,s为对象,new Student()为实例
获得对象的方式:类 对象=实例, 实例即是new 类名(). 通过new在堆空间中申请分配空间,这时获得的对象必须把地址存放在一个对象变量中才能使用。
参数(显式参数和隐式参数)
隐式(impliticit)参数:出现在方法名前面的类对象,即方法调用的目标和接收者,关键词this表示隐式参数;
如:
public void raiseSalary(double byPercent)
{
//Scanner in=new Scanner(System.in);
double raise=this.salary*byPercent/100;
this.salary+=raise;
}
显示(explicit)参数:方法名后括号的数值,出现在方法声明中。
在java中,所有的方法都必须在类内部定义,是否为内联方法则是由jvm设置的任务。
- 封装
封装的定义:为了隐藏对象的属性和实现细节,仅对外公开接口,控制程序中属性的读和修改的访问级别,将抽象的数据或行为(功能)封装成一个类。
- 域访问器(只返回实例域值,又称get方法)
要获取或设置实例域的值,需要一个私有的数据域,一个公有的域访问器方法,一个公有的域更改器方法
2.final实例域:
构造器执行后,域的值被设置且不能被修改(即没有set方法)final修饰的方法参数只能初始化,不能被修改。大都用于基本(primitive)类型域或不可变(immutable)类(类的每个方法都不改变其对象),
Final修饰符一般用于基本类型域和不可变域
如:private final String final StringBuider evalutions将会被初始化成
Evalutations=new StringBuilder evalutions()
- 类的访问权限
Java提供了四种访问权限控制机制
- 包访问权限
当方法或域未给定访问权限限定符时,默认该方法或域成员对包内可见,包外不可见。当一个类对包外可见时,该类的访问权限限定符和构造器限定符均要修改为public
- public访问权限
当方法或域给定public限定符时,则具有该权限控制,对包的内外部完全可见。Java中最多只允许一个文件出现一个public类(该类与java文件名称一致,对外提供接口)若java文件中无public类,说明该类只能在包内调用
- private访问权限
当方法域为private关键字为时可,只允许访问内部类访问。一般情况下将域定义为private,将其方法定义为public 。外界使用该类时,通过public方法使用其接口,而具体的域成员则对外部屏蔽,通过类接口可以间接访问。
具有protected权限的域和方法只能对其自身和导出类可见。
- protected访问权限
可提高
作用域 | 当前类 | 同一package | 子孙类 | 其他package |
public | 1 | 1 | 1 | 1 |
protected | 1 | 1 | 1 | 0 |
fridenly | 1 | 1 | 0 | 0 |
private | 1 | 0 | 0 | 0 |
- 静态域或静态方法
- 静态域
在域定义时将域定义为static(静态域),每个类只有一个静态域,静态域属于类,不属于任何独立的对象。
如给Employee类添加一个实例域id和静态域nextId;
public void raiseSalary(double byPercent)
{
//Scanner in=new Scanner(System.in);
double raise=this.salary*byPercent/100;
this.salary+=raise;
}
这里,每个雇员对象都有自己的一个id域,n个Employee对象则有n个实例域id,只有一个静态域nextId.静态域的值为nextId=1
每个对象的所有实例域都有一份自己的拷贝,这个类的所有实例域共享一个静态域。
- 静态常量
若一个常量是编译期常量,即编译期即可确定常量值,则类在未加载的情况下也能引用该静态常量,因为该静态常量值存储在JVM内存的常量区,在类不加载时即可访问。
如在Math类中定义一个静态常量
public class Math
{
Public static final double Pi=3.1415926;
}
又如常见的静态常量System.out,它在System类中的声明如下
public class System
{
public static final PrintStream out=?
}
但是,并不是所有的静态常量访问都不需要加载类,如果常量值是随机值,则需要加载类。如public static final int FINAL_VAR_INT = new Random(47).nextInt();
静态常量和常量的区别:
A,可以把static final 看做类变量,一个类中只有唯一的一个;|||final看做对象变量,每个对象里面只有唯一一个,并且各个对象的final变量可以不一样
B,用static修饰过的常量会存到常量池里面。存入的方式是:先到常量池里面寻找,看是否该常量已经存在,如果存在说明是编译期常量,返回该常量的引用;不存在就在常量池里面新建一个常量,返回引用。|||如果没有static修饰,就属于对象的属性,绝大多是对象是存在堆中,也就是如果多个对象存在同样一个常量,在堆中会为每一个对象的该常量分配存储空间。由此可以看出,static修饰后的作用就是减少内存的消耗,常量的存储位置发生了变化
C.static的内存只能存在一个域。非static的,n个对象就有n个域
D.在创建对象的时候,static final类变量在类加载的之前就已经存在;|||final对象变量必须在定义时或者构造器中赋初值,即需要每个成员变量(非静态)开辟内存空间如果是基本数据类型的变量,则该数据值初始化后不能更改;如果是引用类型的final变量,则对其初始化后不能将其指向另一个对象,但是所指向的对象的内容是可变的
- 静态方法
静态方法是指被static修饰过的方法在类定义的时候已经被装载和定义,而非静态方法在类定义时没有占用内存,只有在类被实例化为对象后,对象调用该非静态方法时才会被分配内存
静态方法没有this参数,即没有隐式参数。静态方法内部不能调用非静态方法(静态方法内部不可以访问非静态方法的实例域,可访问自身类中的静态域),而非静态方法则可以调用静态方法,在没有创建任何对象的情况下可以调用static类。
静态方法访问自身类中的静态域如:
public static int getNextId()
{return nextId;
}
int n=Employee.getNextId();//通过类名调用方法
静态方法的使用情况
A:一个方法不需要访问对象状态,其所需要的参数有显式参数提供(Math.pow(x,a))
B:一个方法只需要访问类的静态域(Employee.getNextId())
static属于类且不属于类对象的变量和函数
- 工厂方法
静态方法的另外一种常见用途就是使用静态工厂方法来构造对象,如NumberFormat类使用工厂方法生成不同风格的格式化对象
NumberFormat currencyFormatter=NumberFormat.getCurrencyInstance()
double x=0.1;
System.out.print(CurrencyFormat.format(x))
上例不利用构造器的原因是:一是无法命名构造器;二是构造器构建时无法改变所构造的对象类型
- main方法
main方法是一个静态方法,不需要对象调用静态方法
public class Application
{
Public static void main(String [] args)
}
main方法不对任何对象进行操作,静态的main方法将创建并执行程序所需要的对象
应用:
将三个Employee对象写入数组,打印雇员信息,最后打印下一个员工可用的标识码来展现静态方法
package ClassesAndProjects;
/*
* this class is for testing the static method
* @son.26/7/2019
*/
public class StaticTest {
public static void main(String[] args) {
Employee[] staff=new Employee[3];
staff[0]=new Employee("小东",9000);
staff[1]=new Employee("小明",8000);
staff[2]=new Employee("小兰",7000);
//print out the informations about staff
for(Employee e:staff) {
e.setId();
System.out.println("name="+e.getName()+",id="+e.getId()+",salary="+e.getSalary());
}
//调用静态方法
int n=Employee.getNextId();
System.out.println("the nextId="+n);
}
class Employee
{
private static int nextId=1;
private String name;
private double salary;
private int id;
public Employee(String n,double s)
{
name=n;
salary=s;
id=0;
}
public String getName() {
return name;
}
public double getSalary() {
return salary;
}
public int getId()
{
return id;
}
public void setId()
{
nextId=id;
nextId++;
}
public static int getNextId()
{
return nextId;
}
}
}
遇见问题一:No enclosing instance of type SaticTest is a accessible.Must qualify the allocation with an enclosing instance of type StaticTest
无法访问StaticMethod类型的内部类实例。必须分配合适StaticMethod类型的内部类实例。
分析:在java中,类中的静态方法不能直接调用动态方法,只有将某个内部类修饰为静态类才能调用该类的成员变量和成员方法。该程序是动态类public class,而主程序是public static class main为静态类。
解决:最简单的方法是将public class改为public static class,即将动态类修饰为静态类
问题解决链接https://laawayne.iteye.com/blog/2165727
package ClassesAndProjects;
/*
* this class is for testing the static method
* @son.26/7/2019
*/
public class StaticTest {
public static void main(String[] args) {
Employee[] staff=new Employee[3];
staff[0]=new Employee("小东",9000);
staff[1]=new Employee("小明",8000);
staff[2]=new Employee("小兰",7000);
//print out the informations about staff
for(Employee e:staff) {
e.setId();
System.out.println("name="+e.getName()+",id="+e.getId()+",salary="+e.getSalary());
}
//调用静态方法
int n=Employee.getNextId();
System.out.println("the nextId="+n);
}
static class Employee
{
private static int nextId=1;
private String name;
private double salary;
private int id;
public Employee(String n,double s)
{
name=n;
salary=s;
id=0;
}
public String getName() {
return name;
}
public double getSalary() {
return salary;
}
public int getId()
{
return id;
}
public void setId()
{
nextId=id;
nextId++;
}
public static int getNextId()
{
return nextId;
}
//每个类都可以有一个main方法,用于对类进行单元测试。这里独立测试Employee类
public static void main(String[] args) {
Employee e=new Employee("Tom",8000);
System.out.print(e.getName()+""+e.getSalary());
}
}
}
当运行时,运行结果如下
当运行时,运行结果如下