一、构造方法
1、定义
构造方法的目的是为对象中的数据进行初始化。
2、格式:
1)、方法名与类名相同
2)、没有返回值类型,连void都没有
3)、没有具体的返回值
3、举例
class Student { private String name; private int age; Student(){ System.out.println("这是无参的构造方法"); } public void setName(String name){ //getXxx()和setXxx()方法 this.name = name; } public String getName(){ return name; } public void setAge(int age){ this.age = age; } public int getAge(){ return age; } public void show() { System.out.println("姓名:" + name + ",年龄:" + age); } } public class StructureDemo1 { public static void main(String[] args) { Student s = new Student(); //创建对象 //s.show(); } } ****上面的代码只new一个对象,然后运行就会输出"这是无参的构造方法"**** ****此时,也就说明在创建对象的时候会自动运行一个构造方法**** ****在代码中没有这个方法时,Java会自己创建一个无参空方法,并且在构造对象的时候调用它****
4、思考:
我们在学习构造方法之前,也是new对象出来,现在知道了,内部一直都是调用一个无参的构造方法,但是呢
我们在类中并没有区写出来构造方法,所以问题是我们一直用的构造方法来自哪里呢?
注意:
1)、如果我们没有给出构造方法,JVM就会自动的提供一个无参的构造方法给我们。
2)、如果我们给出了构造方法,JVM就不会再提供默认的无参构造方法了。
如果我们没有给出无参构造方法,却给出了其他的带参构造方法,JVM就不再会提供默认的无参构造方法
只要我们给出了构造方法,无论是有参还是无参,JVM就永远不会再提供无参的构造方法
3)、构造方法在同一个类中也是可以发生重载的。(方法名一致,参数列表不一样,与返回值无关)
5、构造方法的作用:
可以在创建对象的时候给对象的成员变量进行赋值
给对象的成员变量进行赋值的两种方式:
1)、使用类提供的公共的setXxx()方法给成员变量进行赋值
2)、使用带参数的构造方法给私有的成员变量进行赋值,
注意形参的变量名和成员变量名一样的时候,需要配合this关键字一起使用
6、构造方法举例
class Structure { private String name; private int age; Structure() { System.out.println("这是我们自己写的无参构造方法"); } Structure(String name) { System.out.println("这是我们自己写的带name参数的带参构造方法"); this.name = name; } Structure(int age){ System.out.println("这是我们自己写的带age参数的带参构造方法"); this.age = age; } Structure(String name,int age){ System.out.println("这是我们自己写的带所有成员变量的参数的带参构造方法"); this.name = name; this.age = age; } public void show() { System.out.println("姓名:" + name + ",年龄:" + age); } } public class StructureDemo2 { public static void main(String[] args) { Structure s1 = new Structure(); System.out.println(s1); ****这里会输出:这是我们自己写的无参构造方法,以及s1的地址值**** ********com.shujia.yhl.day12.Structure@1540e19d**** System.out.println("======================="); //创建第二个对象 Structure s2 = new Structure("吴邪"); s2.show(); //输出吴邪,0 System.out.println("======================="); //创建第三个对象 Structure s3 = new Structure(17); s3.show();//输出null,17 System.out.println("======================="); //创建第四个对象 Structure s4 = new Structure("张起灵",18); s4.show();//输出张起灵,18 } }
二、类的改进
1、类的组成:成员变量,成员方法
2、今天又学习了一个新的成员:构造方法
3、改进一下我们之前写的类:
成员变量
构造方法
成员方法:
根据有无返回值:
1)、没有返回值的成员方法
2)、有返回值的成员方法
根据有无参数:
1)、无参的成员方法
2)、有参数的成员方法
4、举例1
class Student2 { private String name; private int age; Student2() { //无参构造方法 } Student2(String name, int age) { //包含所有成员变量的参数的构造方法 this.name = name; this.age = age; } public void setName(String name) { //公共的getXxx()方法和setXxx()方法 this.name = name; } public String getName() { return name; } public void setAge(int age) { this.age = age; } public int getAge() { return age; } public void printHello() { //无参无返回值的成员方法 System.out.println("HelloWorld"); } public String getTianQi() { //无参有返回值的成员方法 return "晴天"; } public void sum(int a, int b) { //带参无返回值的成员方法 System.out.println(a + b); } public String concat(String s1, String s2) { //带参有返回值的成员方法 return s1 + s2; } public void show() { System.out.println("姓名:" + name + ",年龄:" + age); //定义一个方法将所有的成员变量输出 } } public class StudentDemo { public static void main(String[] args) { Student2 s1 = new Student2(); //使用无参的构造方法创建对象 s1.show(); //调用无参无返回值的成员方法 s1.printHello(); //调用无参有返回值的成员方法 String tianqi = s1.getTianQi(); System.out.println(tianqi); //调用带参有返回值的成员方法 String s = s1.concat("南派三叔","yyds"); System.out.println(s); s1.sum(12,45); //调用带参无返回值的成员方法 Student2 s2 = new Student2("阿玲",26); //使用带参的构造方法创建对象 s2.show(); } }
5、举例2
标准类代码3.0版本,一个标准的手机类
分析:
手机:
属性:品牌,颜色,价格
行为:打电话,发短信,学习
类:
成员变量:brand,color,price(使用private修饰)
构造方法:无参构造方法,带参构造方法
成员方法:setXxx()和getXxx()方法
show():输出所有的成员变量
public class Phone { private String brand; //定义成员变量 private String color; private int price; Phone() { //定义构造方法 } Phone(String brand, String color, int price) { this.brand = brand; this.color = color; this.price = price; } public void setBrand(String brand) { //定义公共的setXxx()和getXxx() this.brand = brand; } public String getBrand() { return brand; } public void setColor(String color) { this.color = color; } public String getColor() { return color; } public void setPrice(int price) { this.price = price; } public int getPrice() { return price; } public void show() { //定义一个可以输出所有成员变量的方法 System.out.println("品牌:" + brand + ",颜色:" + color + ",价格:" + price); } }
手机测试类:
开发中最常用的是第二种方式,单个使用永远要比混合使用要灵活
public class PhoneDemo { public static void main(String[] args) { System.out.println("=======使用无参的构造方法创建对象,并使用setXxx()给成员变量进行赋值======="); Phone p1 = new Phone(); System.out.println("未赋值前:"); p1.show(); p1.setBrand("华为"); p1.setColor("黑色"); p1.setPrice(4999); System.out.println("赋值后:"); p1.show(); System.out.println("================================================"); System.out.println("=======使用带参数的构造方法创建对象,并使用getXxx()获取成员变量======="); Phone p2 = new Phone("锤子手机","白色",8888); String brand = p2.getBrand(); String color = p2.getColor(); int price = p2.getPrice(); System.out.println("品牌["+brand+"]\t颜色["+color+"]\t价格["+price+"]"); } }
三、无参构造方法创建对象的过程
调用无参的构造方法创建对象的过程。
Phone p = new phone
1、将编译好的class文件加载到内存中的方法区中
2、将main方法加载到栈内存中
3、将Phone p在栈内存中开辟空间
4、在堆内存中开辟空间,并且为成员变量赋予系统默认值
5、将堆内存中的对象的地址值赋值给栈中的引用变量
四、带参构造方法创建对象的过程
class Student { private String name = "胡八一"; private int age = 18; student (String name, int age){ this.name = name; this.age = age; } } class StudentDemo { public static void main(String[] args){ student s = new Student("张凯旋",18); } }
1、将编译好的字节码文件加载到方法区中
2、将main方法加载到栈中执行
3、在栈中开辟内存空间给学生变量s
4、在堆内存中为学生对象开辟内存空间
5、给成员变量进行系统默认赋值,nu11, 0
6、给成员变量进行显示的初始化赋值,"胡八一",18
7、通过构造方法给成员变量进行初始化赋值,"张凯旋",17
8、将堆内存中该学生对象的空间内存地址赋值给栈中变量s,学生变量s就指向与堆内存中该学生对象
五、封装举例
题目:定义一个类Demo,其中定义一个求两个数据和的方法,定义一个测试了Test,进行测试。
第一种方式:
class Demo { public int sum() { int a = 11; int b = 22; int c = a + b; return c; } } public class Test1 { public static void main(String[] args) { Demo d = new Demo(); //创建对象 int sum = d.sum(); } }
通过第一种方式实现了我们题目的需求
但是不好,因为加法的数值写固定了,所以不好
改进:可以以后传两个值进来
第二种方式:
class Demo { public int sum(int x, int y) { return x + y; } } public class Test1 { public static void main(String[] args) { Demo d = new Demo(); //创建对象 int sum = d.sum(65,70); System.out.println(sum); } }
第二种方式相比较于第一种方式来说,虽然我们可以进行手动传入参数
但是,实现的过程并没有用到面向对象的封装的知识,我们可以尝试将两个参数定义为成员变量
第三种方式:
class Demo { private int a; private int b; public int sum(int a,int b){ this.a = a; this.b = b; return (this.a+this.b); } } public class Test1 { public static void main(String[] args) { Demo d = new Demo(); //创建对象 int sum = d.sum(65,70); System.out.println(sum); } }
总结:
问题:哪一种方式实现该问题的最优解?
其实,我们在面对业务问题的时候,什么时候考虑使用面向对象的思想,
或者换句话说,什么情况下,将我们问题中的参数定义成成员变量呢?
解答:只有当我们题目中参数与该类形成属性和行为的关系的时候,才定义成成员变量
因为类是用来描述现实世界事物的,所以也就说成员变量是用来描述类
所以,这道题,第二种方式是最优解。
附加
1、快捷键
alt+Insert可以快速调出set和get方法
2、导包
导包的import语句需要放在所有代码的最前面
六、static关键字
1、static关键字
定义一个人的类,成员变量有姓名,年龄国籍,在实例化对象的时候发现,如果有很多人国籍都一样,
譬如都是中国那么每次创建的时候,都会在堆内存开辟一个这样的变量空间,会造成一定的浪费,
那么有没有什么办法,让所有一样国籍的人共用一个国籍的值?
针对多个对象拥有共同的成员变量,值是一样的情况
Java提供了一个关键字给我们处理这样的情况,这个关键字叫做:static
可以修饰成员变量和成员方法,修饰成员变量的时候,可以让多个对象共用一个成员变量的值,
注意:如果堆这些对象中的任一个国籍进行修改那么所有人的国籍都会改变。
class Person{ private String name; //姓名 private int age; //年龄 private String nationality; //国籍 private static String nationality; public Person() { } public Person(String name, int age, String nationality) { this.name = name; this.age = age; this.nationality = nationality; } public Person(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getNationality() { return nationality; } public void setNationality(String nationality) { this.nationality = nationality; } public void show(){ System.out.println("姓名:"+name+",年龄:"+age+",国籍:"+nationality); } } public class PersonDemo { public static void main(String[] args) { Person p1 = new Person("胡八一",26,"中国"); //创建第一个对象 p1.show(); Person p2 = new Person("王凯旋,30); //创建第二个对象 p2.show(); Person p3 = new Person("杨雪莉",18); //创建第三个对象 p3.show(); System.out.println("=========================================="); //修改国籍,那么三个人都会被修改 p1.setNationality("美国"); p1.show(); p2.show(); p3.show(); } }
2、static的特点:它可以修饰成员变量和成员方法
1)、随着类的加载而加载
运行之前会将class类文件加载到方法区中,而被static修饰的成员是随着类的加载而加载,
所以被static修饰的成员在方法区存放,回想一下main方法
2)、优先于对象存在
3)、被类所有的对象共享
举例:所有的中国人的国籍信息都一样
什么时候使用static关键字呢?
如果说明某个成员变量是被所有对象共享的,那么它就应该被定义为静态的。
举例:
哈罗单车:(可以被static修饰)
自己的水杯:(不可以被static修饰)
4)、被静态修饰的成员可以直接通过类名调用
一般情况下,我们今后开发的时候,只要看到类中有静态的成员变量或者成员方法
今后调用的时候,一律用类名.静态成员,这样的方式区调用,推荐使用类名的方式调用,这也是规范。
通过调用的方式不同区分不同的成员
所以被静态修饰的成员,一般情况下被称之为:类成员,与类相关的
4)、举例
class Demo2{ int a = 10; //定义一个非静态的成员变量 static int b = 20; //定义一个静态的成员变量 } public class StaticDemo1 { public static void main(String[] args) { Demo2 d1 = new Demo2(); System.out.println(d1.a); //System.out.println(d1.b); System.out.println(Demo2.b); //和上一句输出一样 } }
3、静态成员访问的问题
1)、static可以修饰成员变量和成员方法
2)、非静态的成员方法:
1)访问的是非静态的成员变量
可以
2)访问的是静态的成员变量
可以
3)访问的是非静态的成员方法
可以
4)访问的是静态的成员方法
可以
总结:非静态的成员方法可以访问静态的成员也可也访问非静态的成员
静态的成员方法:
1)访问的是非静态的成员变量
不可以
2)访问的是静态的成员变量
可以
3)访问的是非静态的成员方法
不可以
4)访问的是静态的成员方法
可以
总结:静态的成员方法只能访问静态的成员