07-01 Java 封装
1:成员变量和局部变量的区别
/* 成员变量和局部变量的区别? A:在类中的位置不同 成员变量:在类中方法外 局部变量:在方法定义中或者方法声明上 B:在内存中的位置不同 成员变量:在堆内存 局部变量:在栈内存 C:生命周期不同 成员变量:随着对象的创建而存在,随着对象的消失而消失 局部变量:随着方法的调用而存在,随着方法的调用完毕而消失 D:初始化值不同 成员变量:有默认初始化值 局部变量:没有默认初始化值,必须定义,赋值,然后才能使用。 注意事项: 局部变量名称可以和成员变量名称一样,在方法中使用的时候,采用的是就近原则。 */ class Varialbe { //成员变量 //int num = 10; int num; //0 public void show() { //int num2 = 20; //局部变量 //可能尚未初始化变量num2 int num2; //没有默认值 // int num2 = 20; System.out.println(num2);//这里报错,num2没有默认值 int num = 100; System.out.println(num); } } class VariableDemo { public static void main(String[] args) { Varialbe v = new Varialbe(); System.out.println(v.num); //访问成员变量 v.show(); } }
2:类作为形式参数的问题
如果你看到一个方法需要的参数是一个类名,就应该知道这里实际需要的是一个具体的对象。
/* 形式参数的问题: 基本类型:形式参数的改变不影响实际参数 引用类型:形式参数的改变直接影响实际参数 */ //形式参数是基本类型 class Demo { public int sum(int a,int b) { return a + b; } } //形式参数是引用类型 class Student { public void show() { System.out.println("我爱学习"); } } class StudentDemo { //如果你看到了一个方法的形式参数是一个类类型(引用类型),这里其实需要的是该类的对象。 public void method(Student s) { //调用的时候,把main方法中的s的地址传递到了这里 Student s = new Student(); s.show(); } } class ArgsTest { public static void main(String[] args) { //形式参数是基本类型的调用 Demo d = new Demo(); int result = d.sum(10,20); System.out.println("result:"+result); System.out.println("--------------"); //形式参数是引用类型的调用 //需求:我要调用StudentDemo类中的method()方法 StudentDemo sd = new StudentDemo(); //创建学生对象 Student s = new Student(); sd.method(s); //把s的地址给到了这里 } }
3:匿名对象
/* 匿名对象:就是没有名字的对象。 匿名对象的应用场景: A:调用方法,仅仅只调用一次的时候。 注意:调用多次的时候,不适合。因为会重新生成对象 那么,这种匿名调用有什么好处吗? 有,匿名对象调用完毕就是垃圾。可以被垃圾回收器回收。 B:匿名对象可以作为实际参数传递 */ class Student { public void show() { System.out.println("我爱学习"); } } class StudentDemo { public void method(Student s) { s.show(); } } class NoNameDemo { public static void main(String[] args) { //带名字的调用 Student s = new Student(); s.show(); s.show(); System.out.println("--------------"); //匿名对象 //new Student(); //匿名对象调用方法 new Student().show(); new Student().show(); //这里其实是重新创建了一个新的对象 System.out.println("--------------"); //匿名对象作为实际参数传递 StudentDemo sd = new StudentDemo(); //Student ss = new Student(); //sd.method(ss); //这里的s是一个实际参数 //匿名对象 sd.method(new Student()); //在来一个 new StudentDemo().method(new Student()); } }
4:封装和private
封装(理解) (1)隐藏实现细节,提供公共的访问方式 (2)好处: A:隐藏实现细节,提供公共的访问方式 B:提高代码的复用性 C:提高代码的安全性 (3)设计原则 把不想让外界知道的实现细节给隐藏起来,提供公共的访问方式 (4)private是封装的一种体现。 封装:类,方法,private修饰成员变量 private关键字(掌握) (1)私有的意义,可以修饰成员变量和成员方法 (2)特点: 被private修饰的后的成员只能在本类中被访问 (3)private的应用: 以后再写一个类的时候: 把所有的成员变量给private了 提供对应的getXxx()/setXxx()方法
private的引出
/* 定义一个学生类: 成员变量:name,age 成员方法:show()方法 我们在使用这个案例的过程中,发现了一个问题: 通过对象去给成员变量赋值,可以赋值一些非法的数据。 这是不合理的。 应该是这个样子的:在赋值之前,先对数据进行判断。 判断到底在哪里做比较合适呢? StudentDemo类是一个测试类,测试类一般只创建对象,调用方法。 所以,这个判断应该定义在Student类中。 而我们在成员变量的位置可不可以进行数据判断呢? 是不可以的,因为做数据校验,必须要依靠一些逻辑语句。 逻辑语句是应该定义在方法中的,所以,我们最终决定在Student类中提供一个方法 来对数据进行校验。 按照我们前面的分析,我们给出了一个方法进行校验。 但是呢,它偏偏不调用方法来赋值,还是直接赋值了, 这样我们的方法就没有起到作用。 我就应该要求你必须使用我的方法,而不能直接调用成员变量赋值。 怎么去强制要求不能直接使用成员变量呢? 针对这种情况,Java就提供了一个关键字 private private:私有的。可以修饰成员变量和成员方法。 注意:被private修饰的成员只能在本类中访问。 其实我讲到现在讲解的是一个封装的思想。 封装:是指隐藏对象的属性和实现细节,仅对外提供公共访问方式。 */ class Student { //姓名 String name; //年龄 private int age; //写一个方法对数据进行校验 /* 返回值类型:void 参数列表:int a */ public void setAge(int a) { if(a < 0 || age > 120) { System.out.println("你给的年龄有问题"); }else { age = a; } } //show()方法,显示所有成员变量值 public void show() { System.out.println("姓名:"+name); System.out.println("年龄:"+age); } } class StudentDemo { public static void main(String[] args) { //创建学生对象 Student s = new Student(); s.show(); System.out.println("--------------"); //给成员变量赋值 s.name = "林青霞"; //s.age = 27; s.setAge(27); s.show(); System.out.println("--------------"); //给age赋值 //s.age = -27; //这个数据是不合理的 //通过方法给值 s.setAge(-27);//你给的年龄有问题 s.show(); System.out.println("--------------"); } }
封装和private的使用
/* 封装和private的应用: A:把成员变量用private修饰 B:提高对应的getXxx()和setXxx()方法 */ //定义学生类 class Student { //姓名 private String name; //年龄 private int age; //姓名获取值 public String getName() { return name; } //姓名设置值 public void setName(String n) { name = n; } //年龄获取值 public int getAge() { return age; } //年龄赋值 public void setAge(int a) { age = a; } } //测试类 class StudentTest { public static void main(String[] args) { //创建学生对象 Student s = new Student(); //使用成员变量 //错误:被私有修饰了,外界不能直接访问了 //System.out.println(s.name+"---"+s.age); System.out.println(s.getName()+"---"+s.getAge()); //给成员变量赋值 //s.name = "林青霞"; //s.age = 27; //通过方法给赋值 s.setName("林青霞"); s.setAge(27); System.out.println(s.getName()+"---"+s.getAge()); } }
6:this关键字和内存图解
(1)代表当前类的引用对象 记住:哪个对象调用方法,该方法内部的this就代表那个对象 (2)this的应用场景: A:解决了局部变量隐藏成员变量的问题 B:其实this还有其他的应用,明天讲解。
this关键字使用
/* 标准的代码改进版 this:哪个对象调用那个方法,this就代表那个对象 */ class Student { private String name; private int age; public String getName() { return name; //这里其实是隐含了this,this.name } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } } class StudentTest2 { public static void main(String[] args) { //创建一个对象 Student s1 = new Student(); s1.setName("林青霞"); s1.setAge(27); System.out.println(s1.getName()+"---"+s1.getAge()); //创建第二个对象 Student s2 = new Student(); s2.setName("刘意"); s2.setAge(30); System.out.println(s2.getName()+"---"+s2.getAge()); } }
this 内存图解:
7:构造方法
7:构造方法(掌握) (1)作用:用于对对象的数据进行初始化 (2)格式: A:方法名和类名相同 B:没有返回值类型,连void都不能有 C:没有返回值 思考题:构造方法中可不可以有return语句呢? 可以。而是我们写成这个样子就OK了:return; 其实,在任何的void类型的方法的最后你都可以写上:return; (3)构造方法的注意事项 A:如果我们没写构造方法,系统将提供一个默认的无参构造方法 B:如果我们给出了构造方法,系统将不再提供默认构造方法 如果这个时候,我们要使用无参构造方法,就必须自己给出。 推荐:永远手动自己给出无参构造方法。 (4)给成员变量赋值的方式 A:setXxx() B:带参构造方法 (5)标准案例 class Student { private String name; private int age; public Student(){} public Student(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; } } 测试: class StudentDemo { public static void main(String[] args) { //方式1 Student s1 = new Student(); s1.setName("林青霞"); s1.setAge(27); System.out.println(s1.getName()+"---"+s1.getAge()); //方式2 Student s2 = new Student("刘意",30); System.out.println(s2.getName()+"---"+s2.getAge()); } }
构造方法
/* 构造方法: 给对象的数据进行初始化 格式: A:方法名与类名相同 B:没有返回值类型,连void都没有 C:没有具体的返回值 */ class Student { private String name; //null private int age; //0
//没有返回值类型,连void都没有 public Student() { System.out.println("这是构造方法"); } } class ConstructDemo { public static void main(String[] args) { //创建对象 Student s = new Student(); System.out.println(s); //Student@e5bbd6 } }
构造方法的重载和注意事项
/* 我们一直在使用构造方法,但是,我们确没有定义构造方法,用的是哪里来的呢? 构造方法的注意事项: A:如果我们没有给出构造方法,系统将自动提供一个无参构造方法。 B:如果我们给出了构造方法,系统将不再提供默认的无参构造方法。 注意:这个时候,如果我们还想使用无参构造方法,就必须自己给出。建议永远自己给出无参构造方法 给成员变量赋值有两种方式: A:setXxx() B:构造方法 */ class Student { private String name; private int age; public Student() { //System.out.println("我给了,你还给不"); System.out.println("这是无参构造方法"); } //构造方法的重载格式 public Student(String name) { System.out.println("这是带一个String类型的构造方法"); this.name = name; } public Student(int age) { System.out.println("这是带一个int类型的构造方法"); this.age = age; } public Student(String name,int age) { System.out.println("这是一个带多个参数的构造方法"); this.name = name; this.age = age; } public void show() { System.out.println(name+"---"+age); } } class ConstructDemo2 { public static void main(String[] args) { //创建对象 Student s = new Student(); s.show(); System.out.println("-------------"); //创建对象2 Student s2 = new Student("林青霞"); s2.show(); System.out.println("-------------"); //创建对象3 Student s3 = new Student(27); s3.show(); System.out.println("-------------"); //创建对象4 Student s4 = new Student("林青霞",27); s4.show(); } }
8、成员方法
/* 类的组成:成员变量,成员方法 今天我们又加入了一个新的成员:构造方法。 以后再提类的组成: 1、成员变量 2、构造方法 3、成员方法 根据返回值: void类型 非void类型 形式参数: 空参方法 非空参方法 */ class Student { public String getString() { return "helloworld"; } public void show() { System.out.println("show"); } public void method(String name) { System.out.println(name); } public String function(String s1,String s2) { return s1+s2; } } class StudentDemo { public static void main(String[] args) { //创建对象 Student s = new Student(); //调用无参无返回值方法 s.show(); //调用无参有返回值方法 String result = s.getString(); System.out.println(result); //调用带参无返回值的方法 s.method("林青霞"); //调用带参带返回值的方法 String result2 = s.function("hello","world"); System.out.println(result2); } }
9、一个标准学生类的代码及测试
/* 一个标准代码的最终版。 学生类: 成员变量: name,age 构造方法: 无参,带两个参 成员方法: getXxx()/setXxx() show():输出该类的所有成员变量值 给成员变量赋值: A:setXxx()方法 B:构造方法 输出成员变量值的方式: A:通过getXxx()分别获取然后拼接 B:通过调用show()方法搞定 */ class Student { //姓名 private String name; //年龄 private int age; //构造方法 public Student() { } //构造方法 public Student(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 void show() { System.out.println(name+"---"+age); } } //测试类 class StudentTest { public static void main(String[] args) { //方式1给成员变量赋值 //无参构造+setXxx() Student s1 = new Student(); s1.setName("林青霞"); s1.setAge(27); //输出值 System.out.println(s1.getName()+"---"+s1.getAge()); s1.show(); System.out.println("----------------------------"); //方式2给成员变量赋值 Student s2 = new Student("刘意",30); System.out.println(s2.getName()+"---"+s2.getAge()); s2.show(); } }
10、代码:Student s = new Student();做了哪些事情?
创建对象做了哪些事情?
(1)把Student.class文件加载到内存 (2)在栈内存为s开辟空间 (3)在堆内存为学生对象申请空间 (4)给学生的成员变量进行默认初始化。null,0 (5)给学生的成员变量进行显示初始化。林青霞,27 (6)通过构造方法给成员变量进行初始化。刘意,30 (7)对象构造完毕,把地址赋值给s变量
图解:
11、变量什么时候定义为成员变量:
如果这个变量是用来描述这个类的信息的,那么,该变量就应该定义为成员变量。
变量到底定义在哪里好呢?
变量的范围是越小越好。因为能及时的被回收。
//方式1
/*
class Demo {
public int sum() {
int a = 10;
int b = 20;
int c = a + b;
return c;
}
}
*/
//方式1满足了我们的要求,但是不好。
//因为参与操作的数据现在是固定的。
//方式2 --推荐
/*
class Demo {
public int sum(int a,int b) {
return a + b;
}
}
*/
//方式2可以满足我们的要求,但是呢我们学习过来面向对象的思想。
//我就再想,a,b可不可以定义为成员变量呢?
//如果可以,我们再改进一版
class Demo {
int a;
int b;
public int sum() {
return a + b;
}
}
//虽然这种方式可以,并且好像是符合了面向对象的思想。
//但是不好。
//因为我们曾经说过:类是一组相关的属性和行为的集合。
//并且类是通过事物转换过来的
//而类中的成员变量就是事物的属性
//属性是用来描述事物的
//同理:成员变量其实是用来描述类的。
长宽是描述长方形这个类的信息,应该定义为成员变量:
/* 定义一个长方形类,定义 求周长和面积的方法, 然后定义一个测试了Test2,进行测试。 长方形的类: 成员变量: 长,宽 成员方法: 求周长:(长+宽)*2; 求面积:长*宽 注意: import必须出现在所有的class前面。 */ import java.util.Scanner; class ChangFangXing { //长方形的长 private int length; //长方形的宽 private int width; public ChangFangXing(){} //仅仅提供setXxx()即可 public void setLength(int length) { this.length = length; } public void setWidth(int width) { this.width = width; } //求周长 public int getZhouChang() { return (length + width) * 2; } //求面积 public int getArea() { return length * width; } } class Test2 { public static void main(String[] args) { //创建键盘录入对象 Scanner sc = new Scanner(System.in); System.out.println("请输入长方形的长:"); int length = sc.nextInt(); System.out.println("请输入长方形的宽:"); int width = sc.nextInt(); //创建对象 ChangFangXing cfx = new ChangFangXing(); //先给成员变量赋值 cfx.setLength(length); cfx.setWidth(width); System.out.println("周长是:"+cfx.getZhouChang()); System.out.println("面积是:"+cfx.getArea()); } }
12、static关键字
10:static关键字(理解) (1)静态的意思。可以修饰成员变量和成员方法。 (2)静态的特点: A:随着类的加载而加载 B:优先与对象存在 C:被类的所有对象共享 这其实也是我们判断该不该使用静态的依据。 举例:饮水机和水杯的问题思考 D:可以通过类名调用 既可以通过对象名调用,也可以通过类名调用,建议通过类名调用。 (3)静态的内存图 静态的内容在方法区的静态区 (4)静态的注意事项; A:在静态方法中没有this对象 B:静态只能访问静态(代码测试过) (5)静态变量和成员变量的区别 A:所属不同 静态变量:属于类,类变量 成员变量:属于对象,对象变量,实例变量 B:内存位置不同 静态变量:方法区的静态区 成员变量:堆内存 C:生命周期不同 静态变量:静态变量是随着类的加载而加载,随着类的消失而消失 成员变量:成员变量是随着对象的创建而存在,随着对象的消失而消失 D:调用不同 静态变量:可以通过对象名调用,也可以通过类名调用 成员变量:只能通过对象名调用 (6)main方法是静态的 public:权限最大 static:不用创建对象调用 void:返回值给jvm没有意义 main:就是一个常见的名称。 String[] args:可以接收数据,提供程序的灵活性 格式:java MainDemo hello world java java MainDemo 10 20 30
static的引入
/* 定义一个人类 姓名和年龄都是变化的,这个我能接收,因为每个人的姓名和年龄是不同的。 但是,我们现在选取的几个人都是中国人,他们的国籍是一样的。 一样的国籍,我每次创建对象,在堆内存都要开辟这样的空间, 我就觉得有点浪费了。怎么办呢? 针对多个对象有共同的这样的成员变量值的时候, Java就提高了一个关键字来修饰:static。 */ class Person { //姓名 String name; //年龄 int age; //国籍 //String country; static String country; public Person(){} public Person(String name,int age) { this.name = name; this.age = age; } public Person(String name,int age,String country) { this.name = name; this.age = age; this.country = country; } public void show() { System.out.println("姓名:"+name+",年龄:"+age+",国籍:"+country); } } class PersonDemo { public static void main(String[] args) { //创建对象1 Person p1 = new Person("邓丽君",16,"中国"); p1.show(); //创建对象2 //Person p2 = new Person("杨幂",22,"中国"); //p2.show(); Person p2 = new Person("杨幂",22); p2.show(); //创建对象3 //Person p3 = new Person("凤姐",20,"中国"); //p3.show(); Person p3 = new Person("凤姐",20); p3.show(); p3.country = "美国";//其他对象里的国籍也会变成“美国” p3.show(); p1.show(); p2.show(); } }
static特点
/* static的特点:(它可以修饰成员变量,还可以修饰成员方法) A:随着类的加载而加载 回想main方法。 B:优先于对象存在 C:被类的所有对象共享 举例:咱们班级的学生应该共用同一个班级编号。 其实这个特点也是在告诉我们什么时候使用静态? 如果某个成员变量是被所有对象共享的,那么它就应该定义为静态的。 举例: 饮水机(用静态修饰) 水杯(不能用静态修饰) D:可以通过类名调用 其实它本身也可以通过对象名调用。 推荐使用类名调用。 因为:静态修饰的内容一般我们称其为:与类相关的,类成员 */ class Student { //非静态变量 int num = 10; //静态变量 static int num2 = 20; } class StudentDemo { public static void main(String[] args) { Student s = new Student(); System.out.println(s.num); System.out.println(Student.num2);//推荐类名调用 System.out.println(s.num2); } }
static内存图解:
static注意事项;
/* static关键字注意事项 A:在静态方法中是没有this关键字的 如何理解呢? 静态是随着类的加载而加载,this是随着对象的创建而存在。 静态比对象先存在。先存在的,不能访问后面进来的。 B:静态方法只能访问静态的成员变量和静态的成员方法 静态方法: 成员变量:只能访问静态变量 成员方法:只能访问静态成员方法 非静态方法: 成员变量:可以是静态的,也可以是非静态的 成员方法:可是是静态的成员方法,也可以是非静态的成员方法。 简单记: 静态只能访问静态。 */ class Teacher { public int num = 10; public static int num2 = 20; public void show() { System.out.println(num); //隐含的告诉你访问的是成员变量 System.out.println(this.num); //明确的告诉你访问的是成员变量 System.out.println(num2); //function(); //function2(); } public static void method() { //System.out.println(num);//无法从静态上下文中引用非静态 变量 num System.out.println(num2); //无法从静态上下文中引用非静态 方法 function() //function(); function2(); } public void function() { } public static void function2() { } } class TeacherDemo { public static void main(String[] args) { //创建对象 Teacher t = new Teacher(); t.show(); System.out.println("------------"); t.method(); } }
main方法的格式
/* main方法的格式讲解: public static void main(String[] args) {...} public:公共的,访问权限是最大的。由于main方法是被jvm调用,所以权限要够大。 static:静态的,不需要创建对象,通过类名就可以。方便jvm的调用。 void:因为我们曾经说过,方法的返回值是返回给调用者,而main方法是被jvm调用。你返回内容给jvm没有意义。 main:是一个常见的方法入口。我见过的语言都是以main作为入口。 String[] args:这是一个字符串数组。值去哪里了? 这个东西到底有什么用啊?怎么给值啊? 这个东西早期是为了接收键盘录入的数据的。 格式是: java MainDemo hello world java */ class MainDemo { public static void main(String[] args) { //System.out.println(args); //[Ljava.lang.String;@175078b //System.out.println(args.length); //0 //System.out.println(args[0]); //ArrayIndexOutOfBoundsException //接收数据后 System.out.println(args); System.out.println(args.length); //System.out.println(args[0]); for(int x=0; x<args.length; x++) { System.out.println(args[x]); } } }
运行结果: