Java基础:Day_06 类、构造器和面向对象
一、对象与类
1.对象与类的关系
对象:对象是人们进行研究任何事物,一切事物都可认为是对象。对象具有状态和行为。
类:具有相同特性(状态)和行为(功能)的对象的抽象就是类。
一句话:对象的类的个性,类是对象的共性。
在面向对象编程里,我们肯定是先有类的。
如果没有类,对象就没法实例化。对象是类的实例,类是对象的模板。如下图,我们可以在类中找到对象的共性,如姓名年龄,以及他们的行为,但是对象是个例。
那么我们可以在Java中这样写一个类:
//人类
class Person
{
String name;
int age;
int height;
int weight;
void sing(){}
void dance(){}
}
//但是当我们要创建一个象这样特殊的“类”,对其进行实例化的时候,这个时候它就叫做对象了。
2.对象和类的创建
1):根据类来创建对象的语法:
类名 变量 = new 类名();
2):给字段设置值:
对象变量.字段名 = 该类型的值;
3):获取字段的值:
该字段类型 变量 = 对象变量.字段值;
4):通过对象调用方法.
对象变量.方法(实参);
实例如下:
class ServantDemo
{
String name;
int age;
void shop()
{
System.out.println("Shoping...");
}
void wash()
{
System.out.println("Washing...");
}
}
class Demo
{
public static void main(String[] args)
{
ServantDemo Wang = new ServantDemo();//实例化该类,创建对象Wang
Wang.name = "Wang Xiaoming"; //为对象Wang赋值
System.out.println(Wang.name);
Wang.age = 23; //为对象Wang赋值
System.out.println(Wang.age);
Wang.shop(); //调用其中的方法
System.out.println("-------------------------");
ServantDemo Liu = new ServantDemo();
Liu.name = "Liu Cixin";
Liu.age = 56;
System.out.println(Liu.name); //输出Liu Cixin
Liu = Wang;
System.out.println(Liu.name); //输出Wang Xiaoming
Wang = null;
System.out.println(Wang.name); //Exception in thread "main" java.lang.NullPointerException
}
}
二、构造器的使用
假如我们想在教务系统中创建一个学生对象的时候,代码如下:
Student s1 = new Student();//特别像在调用一个方法名称为Student的无参数方法.
我们把这种特殊的方法,称之为构造方法,构造器(构造函数/构造子:Constructor),但是我们发现在Student类中,却找不到该构造方法,却又没有报错。编译器在编译源文件的时候,会创建一个缺省的构造器。
构造器的作用:
1):创建对象,凡是必须和 new 一起使用。
2):完成对象的初始化操作。
构造器的特点:
1):构造器的名称和当前所在类的名称相同.
如果构造器名称叫XXX, new XXX();你看出来创建的是什么类型的对象吗?
2):禁止定义返回类型,千万不要使用void作为返回类型. void Student(){}这是普通方法。
3):在构造器中,不需要使用return。 其实构造器是有返回值的,返回的是当前创建对象的引用。
编译器创建的默认构造器的特点:
1):符合构造器特点.
2):无参数的.
3):无方法体.
4):如果类A没有使用public修饰,则编译器创建的构造器也没有public修饰.
使用了public修饰,则编译器创建的构造器也使用public修饰.
构造器:如果我们没有显示提供构造器,则编译器在编译时创建一个缺省的构造器。但是,如果我们显示定义了一个构造器,则编译器不再创建默认构造器。
推论:某一个类,至少存在一个构造器。
没有使用自定义构造器如下:
class PersonDemo
{
String name = null;
//Person(String n)//自定义构造器
//{
// name = n; //把n作为参数赋值给name。
//}
}
class Demo
{
public static void main(String[] args)
{
//创建对象:其实的调用构造器
PersonDemo p1 = new PersonDemo();
p1.name = "Will";
System.out.println(p1.name);
//强行使用会有:
// 错误: 无法将类 PersonDemo中的构造器 PersonDemo应用到给定类型;
}
}
使用了自定义构造器如下:
class PersonDemo
{
String name = null;
PersonDemo(String n)//自定义构造器
{
name = n; //把n作为参数赋值给name。
}
}
class Demo
{
public static void main(String[] args)
{
//创建对象:其实的调用构造器
PersonDemo p1 = new PersonDemo("will");
System.out.println(p1.name);
}
}
可以看到我们自定义构造器的效果。
当然,和方法一样,构造器在称为构造方法的同时,依然拥有方法的重载。
三、static关键字
当我们创建“人”这个对象都有name和age两个状态,但是不同对象的name的值和age的值是不一样的。也就是说,不同对象的name和age是不一样的。
但是在生活中有些东西不是属于某一个具体对象的,而是属于整个事物的类型,比如:全世界人的总数,人类的毁灭的行为,应该属于人类,而不属于某一个人。
状态和行为应该有对象和类型之分.
有的状态和行为应该属于对象,不同的对象,状态和行为可以不一样。
有的状态和行为应该属于类型,不属于对象。
问题:现在如何表示人类毁灭这个行为(destory)。
要解决该问题,得学习static修饰符。
关于修饰符(形容词),来看几个案例。
晚上11点,班长在写代码,遇到一个bug,班长解决问题后,睡觉去了。
晚上11点,班长漫不经心地在写代码,遇到一个很简单的bug,班长稀里糊涂解决问题后,满不在乎地睡觉去了。
晚上11点,帅气的班长认认真真地在写代码,遇到一个超难的bug,班长呕心沥血解决问题后,眉开眼笑地睡觉去了。
我们可以看到修饰符是需要对对象进行修饰的。
static修饰符表示静态的,可修饰字段、方法、内部类。
其修饰的成员属于类,也就是说static修饰的资源属于类级别,而不是对象级别。
static的真正作用:用来区别字段,方法,内部类,初始化代码块是属于对象还是属于类本身。
static修饰符的特点:
1.static修饰的成员(字段/方法),随着所在类的加载而加载。当JVM把字节码加载进入JVM时,static修饰的成员已经在内存当中。(static成员会先与JVM)
2.优先于对象的存在
对象时我们通过new关键字创建出来的
3.static修饰的成员被该类型的所有对象所共享。根据该类创建出来的任何对象,都可以访问static成员。。表面上通过对象去访问static成员,其本质依然是使用类名访问,
和对象没有任何关系。
4.因为static修饰的成员不属于对象,所以可以直接使用类名访问static成员。
成员和实例成员的访问 :
类中的成员:字段,方法,内部类.
类成员: 使用static修饰的成员.
实例成员: 没有使用static修饰的成员.
类成员只能访问类成员,实例成员只能访问实例成员.
类成员,直接属于类, 可以通过类来访问static字段和static方法.
实例成员,只属于对象, 通过对象来访问非static字段和非static方法.
(对象其实可以访问类成员,但是底层依然使用类名访问的.)
在static方法中,只能调用static成员。
非static方法,可以访问静态成员,也可以访问实例成员。
//Demo.java:7: 错误: 无法从静态上下文中引用非静态 变量 index
class Demo
{
int index = 17;//index没有static修饰,属于对象,只能用对象访问。
public static void main(String[] args)
{
System.out.println(index);
}
}
但是我们将代码改为:
class Demo
{
int index = 17;
public static void main(String[] args)
{
Demo d = new Demo();
System.out.println(d.index);
//System.out.println(new Demo().index);上一句也可以这么写
//里面包含了一个匿名内部类。
}
}
当然如果加上了static:
class Demo
{
static int index = 17;
public static void main(String[] args)
{
System.out.println(index);
}
}
案例2:
class MyObjectDemo
{
private static String str1 = "String_1";
private static String str2 = "String_2";
public void print_1()
{
System.out.println(str1);
System.out.println(str2);
}
public static void print_2()
{
System.out.println(str1);
System.out.println(str2);
}
}
class Demo
{
public static void main(String[] args)
{
MyObjectDemo.print_2();//static方法直接用类名调用,也可以是new了之后,用obj调用
//但是print_1不行,因为他没有用static修饰
}
}
但是,假如将MyObjectDemo类中的str2的static去掉之后。
class MyObjectDemo
{
private static String str1 = "String_1";
private String str2 = "String_2";
public void print_1()
{
System.out.println(str1);
System.out.println(str2);
}
public static void print_2()
{
System.out.println(str1);
System.out.println(str2);//此处将无法引用str2
}
}
class Demo
{
public static void main(String[] args)
{
MyObjectDemo.print_2();
}
}
对于方法还有:
class MyObjectDemo
{
private static String str1 = "String_1";
private static String str2 = "String_2";
public void print_1()
{
System.out.println(str1);
System.out.println(str2);
print_2(); //非static可以访问static
}
public static void print_2()
{
System.out.println(str1);
System.out.println(str2);
print_1(); //static不能访问非static
}
}
class Demo
{
public static void main(String[] args)
{
//MyObjectDemo obj = new MyObjectDemo();
MyObjectDemo.print_2();
}
}
总结以上:
1.对于static方法:static方法可以只能调用static成员,而非static无要求。如static修饰的Print_2只能调用被static修饰的str1,不能调用未经static修饰的str2。(自家人只和自家人玩)
2.static变量:static变量被所有的对象所共享(诞生时就产生的个人“标志”),在内存中只有一个副本,它当且仅当在类初次加载时会被初始化。而非static变量是对象所拥有的,在创建对象的时候被初始化,存在多个副本,各个对象拥有的副本互不影响。
static成员变量的初始化顺序按照定义的顺序进行初始化。
3.static代码块:形成静态代码块以优化程序性能。static块可以置于类中的任何地方,类中可以有多个static块。在类初次被加载的时候,会按照static块的顺序来执行每个static块,并且只会执行一次。因此,很多时候会将一些只需要进行一次的初始化操作都放在static代码块中进行。(static代码块后面再说)
什么时候定义成static的字段和方法:
如果这一个状态/行为属于整个事物(类),就直接使用static修饰被所有对象所共享。
如果不使用static修饰,则这些方法属于该类的对象,我们得先创建对象再调用方法,在开发中工具对象只需要一份即可,可能创建N个对象,此时我们往往把该类设计为单例的.,但是还是有点麻烦。
一般的,在开发中设计工具方法,为了调用简单,我们使用static修饰。
因为加了static就可以使用类名调用 ,如果没有加static那么只能只用对象名称调用。
类成员的使用
利处:对对象的共享数据进行单独空间的存储,节省空间,没有必要每一个对象中都存储一份,可以直接被类名调用。
弊端:生命周期过长。