之前在C++中已经讲述过面向对象的概念,其实类就像模板,对象就是实体,通过一个类就可以产生多个对象。
一、类和类的实例化
用类类型创建对象的过程就成为类的实例化
1、类的定义
一个类由字段和方法组成,没有字段和方法的类不实用,例如:
class Person{
//字段
public String name;
public int age;
static int size = 0;//静态成员变量
//方法
public void eat(){
System.out.println("吃饭");
}
public void sleep(){
System.out.println("睡觉");
}
//静态方法
public static void func(){
System.out.println("静态方法");
}
}
此时实例化一个Person对象,例如:
public class TestDemo2 {
public static void main(String[] args) {
Person per = new Person();
}
}
per在栈上,new的对象在堆上,实例成员变量(name,age)都在对象中,静态成员变量不在栈上而是在方法区中。
2、访问对象当中的实例成员变量
例如我们new了一个Person对象,此时要访问name和age,例如:
class Person{
//字段
public String name = "Daisy";
public int age = 18;
static int size = 0;//静态成员变量
//方法
public void eat(){
System.out.println("吃饭");
}
public void sleep(){
System.out.println("睡觉");
}
//静态方法
public static void func(){
System.out.println("静态方法");
}
}
public class TestDemo2 {
public static void main(String[] args) {
Person per = new Person();
per.name = "Peter";
per.age = 20;
System.out.println(per.name);
System.out.println(per.age);
}
}
通过‘.’表示对象的引用访问实例成员变量,如果实例成员没有进行初始化,默认值为对应的0值,引用类型默认为null,char类默认为’\u0000’,boolean类型默认为false;上述我给name初始化为Daisy,age初始化为18,使用’.'访问实例成员变量并且将name改为Peter,age改为20,结果为:
3、访问静态数据成员
由于静态成员变量不属于对象,存在方法区中属于类,因此要通过类名进行访问,例如:
class Person{
//字段
public String name = "Daisy";
public int age = 18;
static int size = 0;//静态成员变量
//方法
public void eat(){
System.out.println("吃饭");
}
public void sleep(){
System.out.println("睡觉");
}
//静态方法
public static void func(){
System.out.println("静态方法");
}
}
public class TestDemo2 {
public static void main(String[] args) {
Person per = new Person();
System.out.println(Person.size);
}
}
此时结果就为0,使用类名Person+’.'访问静态成员变量size并且打印。
注意:
static修饰的成员变量存在方法区中,且只有一份,静态方法不能调用非静态方法,例如:
静态方法func调用非静态方法sleep就会报错。
二、封装
我们在开发时经常会涉及两个角色,也就是类的实现者和类的调用者,例如上述的Person类就是类的实现,而Person类下面的主函数为对类进行调用,而封装的本质就是让类的调用者不用知道类的内部是如何实现的,在使用时只需要进行调用,此时就需要使用封装。
1、用private修饰属性和方法
例如:
class Person{
//字段
private String name = "Daisy";
private int age = 18;
public void show(){
System.out.println("我叫" + name + "," + "今年" + age + "岁");
}
}
public class TestDemo2 {
public static void main(String[] args) {
Person per = new Person();
per.show();
}
}
如果使用public来修饰这两个字段,那么类的调用者要对内部的实现进行了解才能够调用,并且如果类的实现者对其进行修改,那么类的调用者也要对代码进行修改,成本较高,而使用private修饰字段,将其封装在类中,类的调用者不用关心类的内部如何使用,只需要调用对应的public方法,并且一般设为public的方法一般比较稳定不会进行修改。
当然使用private也可以修饰方法,一般情况我们会将属性设置为private,方法是否是public的视情况而定。
2、getter方法和setter方法
当我们使用private修饰字段时,那么就无法使用这个字段了,例如:
此时运行代码就会出错,那么如果我们此时需要获取或者设置这个字段,就要使用getter和setter方法来获得/设置private修饰的字段,例如:
class Person{
//字段
private String name = "Daisy";
private int age = 18;
public String getName() {
return name;
}
public int getAge() {
return age;
}
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
}
public class TestDemo2 {
public static void main(String[] args) {
Person per = new Person();
per.setName("Peter");
per.setAge(20);
System.out.println("名字为:"+per.getName());
System.out.println("年龄为:"+per.getAge());
}
}
此时的运行结果为:
使用getter和setter方法就可以在类外调用这两个方法来访问private的成员变量,例如此时我将name通过setter方法改为Peter,将age改为20,再使用getter方法获得这两个成员变量并且打印。
注意:
this关键字的作用:如果此时setter方法中不使用this关键字,例如:
class Person{
//字段
private String name = "Daisy";
private int age = 18;
public String getName() {
return name;
}
public int getAge() {
return age;
}
public void setName(String name) {
name = name;
}
public void setAge(int age) {
age = age;
}
}
public class TestDemo2 {
public static void main(String[] args) {
Person per = new Person();
per.setName("Peter");
per.setAge(20);
System.out.println("名字为:"+per.getName());
System.out.println("年龄为:"+per.getAge());
}
}
此时的运行结果就为:
发现此时并没有将name改为Peter,age改为20,因为我们会发现在setter方法中是name给name赋值,age给age赋值,所以调用setter方法并不会将指定的值赋给当前对象的成员变量,因此要使用this关键字,表示当前对象的引用。
三、构造方法
一个对象的产生分为两步:
(1)为对象分配内存;
(2)调用合适的构造方法
构造方法名字和类名相同,没有返回值类型,每一个类中至少存在一个构造方法,当没有提供构造方法时,编译器会自动生成一个不带参数的构造方法,一旦手动提供构造方法编译器就不会自动生成了。
例如:
class Student{
private String name;
private int age;
//无参构造
public Student(){
this.name = "Daisy";
this.age = 18;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
}
public class TestDemo3 {
public static void main(String[] args) {
Student st = new Student();
System.out.println(st.getName());
System.out.println(st.getAge());
}
}
此时结果就为:
注意:构造方法完成之后,对象才产生,而在构造方法中,用到了this关键字来实现构造,因此this并不代表对象,而代表当前对象的引用。
构造方法支持重载,例如:
class Student{
private String name;
private int age;
//无参构造
public Student(){
this.name = "Daisy";
this.age = 18;
}
public Student(String name,int age){
this.name = name;
this.age = age;
}
public Student(String name){
this.name = name;
}
public Student(int age){
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
}
public class TestDemo3 {
public static void main(String[] args) {
Student st1 = new Student();
System.out.println(st1.getName());
System.out.println(st1.getAge());
Student st2 = new Student("Daisy",18);
System.out.println(st2.getName());
System.out.println(st2.getAge());
Student st3 = new Student("Daisy");
System.out.println(st3.getName());
System.out.println(st3.getAge());
Student st4 = new Student(18);
System.out.println(st4.getName());
System.out.println(st4.getAge());
}
}
此时结果为:
st1调用无参构造,st2调用全参构造,st3调用只有name的参数构造,st4调用只有age的参数构造。
注意:
this():调用自己的构造方法,只能在构造方法中写且只能调用一次并且只能写到构造方法的第一行,例如
class Student{
private String name;
private int age;
//无参构造
public Student(){
this("Daisy",18);//调用带有全参的构造方法,也就是public Student(name,age)
}
public Student(String name,int age){
this.name = name;
this.age = age;
}
public Student(String name){
this.name = name;
}
public Student(int age){
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
}
public class TestDemo3 {
public static void main(String[] args) {
Student st1 = new Student();
System.out.println(st1.getName());
System.out.println(st1.getAge());
Student st2 = new Student("Daisy",18);
System.out.println(st2.getName());
System.out.println(st2.getAge());
Student st3 = new Student("Daisy");
System.out.println(st3.getName());
System.out.println(st3.getAge());
Student st4 = new Student(18);
System.out.println(st4.getName());
System.out.println(st4.getAge());
}
}
结果也一样。
this.字段名:调用当前对象的属性
this.方法名:调用当前对象的方法。
四、代码块
1、实例代码块(构造代码块)
例如此时给一个实例代码块将name初始化为Daisy,age初始化为18,再给一个无参构造函数使用name和age的默认值(null和0):
class Student{
private String name;
private int age;
//无参构造
public Student(){
}
//使用代码块进行初始化
{
this.name = "Daisy";
this.age = 18;
System.out.println("实例代码块");
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
}
public class TestDemo3 {
public static void main(String[] args) {
Student st1 = new Student();
System.out.println(st1.getName());
System.out.println(st1.getAge());
}
}
此时运行结果为:
可以看到实例代码块是优于构造函数执行的。
2、静态代码块
例如实现
class Student{
private String name;
private int age;
private static int count = 0;//静态成员变量
//无参构造
public Student(){
System.out.println("构造方法");
}
//使用代码块进行初始化
{
this.name = "Daisy";
this.age = 18;
System.out.println("实例代码块");
}
static{
count = 20;
System.out.println("静态代码块");
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
public static int getCount() {
return count;
}
}
public class TestDemo3 {
public static void main(String[] args) {
Student st1 = new Student();
System.out.println(st1.getName());
System.out.println(st1.getAge());
System.out.println(Student.getCount());
}
}
结果为
可以看到调用的顺序是:
(1)静态代码块
(2)实例代码块
(3)构造方法
并且注意:静态的方法也是不依赖于对象的,可以通过类名来进行访问。
注意:静态代码块不能访问非静态成员。
3、本地代码块
也就是在方法中给一个代码块(这种一般不会使用,没有意义)
五、匿名对象
匿名对象就是没有名字的对象,也就是没有引用的对象叫做匿名对象,匿名对象只有在创建对象的时候使用,如果一个对象只使用一次后面不会再用,可以考虑使用匿名对象。
例如:
class Info{
private String name;
private int age;
public Info(String name,int age){
this.name = name;
this.age = age;
}
public void show(){
System.out.println(name);
System.out.println(age);
}
}
public class TestDemo4 {
public static void main(String[] args) {
new Info("Daisy",18).show();
}
}
此时结果为: