1. 封装概述(封装与隐藏)
封装:隐藏事物的属性和实现细节,对外提供公共的访问方式。
为什么需要封装?封装的作用和含义?
-
我要用洗衣机,只需要按一下开关和洗涤模式就可以了。有必要了解洗衣机内部的结构吗?有必要碰电动机吗?
-
我要开车,...
我们程序设计追求“高内聚,低耦合”。
-
高内聚 :类的内部数据操作细节自己完成,不允许外部干涉;
-
低耦合 :仅对外暴露少量的方法用于使用。
-
耦合性。
封装性的设计思想
隐藏对象内部的复杂性,只对外公开简单的接口。便于外界调用,从而提高系统的可扩展性、可维护性。通俗的说,把该隐藏的隐藏起来,该暴露 的暴露出来。这就是封装性的设计思想。
封装的好处:
-
隐藏了事物的实现细节
-
提高了代码的复用性
-
提高了安全性
封装的原则:
-
隐藏事物的属性
-
隐藏事物的实现细节
-
对外提供公共的访问方式
-
代码体会:
-
public class Animal { String name; int legs; void move() { System.out.println("我叫" + name + ",我用" + legs + "条腿跑步!"); } } public class AnimalTest { public static void main(String[] args) { Animal pig = new Animal(); pig.name = "佩奇"; pig.legs = 4; // 修改一下试试? pig.move(); } }
-
使用者对类内部定义的属性(对象的成员变量)的直接操作会导致数据的错误、混乱或安全性问题。
2. private关键字
-
含义:私有的
-
可以修饰的内容:
-
修饰成员变量
-
修饰成员方法
-
修饰构造方法
-
修饰内部类
-
-
修饰之后的效果:被
private
修饰的成员,只能在本类中被访问。 -
注意事项:
private
只是封装的一种体现形式,封装还可以使用其他的修饰符来完成。 -
示例:
-
public class Animal { private String name; private int legs; void move() { System.out.println("我叫" + name + ",我用" + legs + "条腿跑步!"); } }
3. Getter & Setter方法
-
当属性被私有之后,外界无法直接访问,所以需要提供公共的访问方式,让外界可以间接的访问属性。对于当前类,就可以控制外界访问属性的方式。(我让你怎么访问,你就只能怎么访问)
-
一般提供
get
方法,获取成员变量的值;提供set
方法,设置成员变量的值。 -
示例:
-
public class Animal { private String name; private int legs; public String getName() { return name; } public int getLegs() { return legs; } public void setName(String n) { name = n; } public void setLegs(int l) { if (l >= 0 && l % 2 == 0) { legs = l; } } public void move() { System.out.println("我叫" + name + ",我用" + legs + "条腿跑步!"); } } //********************************************* public class AnimalTest { public static void main(String[] args) { Animal pig = new Animal(); pig.setName("佩奇"); pig.setLegs(-3); pig.move(); } }
-
以上是封装性的体现之一,但是它并不等同于封装性!!!这点需要牢记,关于封装性的体现还有其他方面等等。比如我们将某个方法添加上private关键字,那么该方法只作为内部使用,并不会对外暴露。你是好人并不等同于你真的好!
-
Java规定了4种权限(从小到大排列):private、缺省、protected、public
-
-
4种权限可以用来修饰类及类的内部结构:属性、方法、构造器、内部类
-
对于class的权限修饰只可以用public和default(缺省), public类可以在任意地方被访问,default类只可以被同一个包内部的类访问。
-
开心一笑
-
A man and woman are in a computer programming lecture. The man touches the woman's hand.
"Hey!" she says. "Those are private!"
The man says, "But we're in the same class!"
4. 变量访问原则和this关键字
-
对于先前标识符的使用我们还是希望可以见明知意:我们将setName与setLegs方法中的形参都变成name和legs,我们发现方法体中的赋值出现了问题。产生问题的原因就是因为变量的访问机制。
-
变量访问原则:
-
就近原则:当在访问某个变量名称的时候,会先寻找最近的该变量名称的定义,如果寻找到了,就使用该变量,如果没有找到,才到更远的位置寻找该变量名称的定义。
-
当局部变量和成员变量同名的时候,一定是先使用局部位置定义的变量,如果没有,才会使用成员位置定义的变量。
-
-
this
关键字:-
表示当前类型当前对象的引用:哪个来调用this所在的方法,this就代表哪个对象
-
作用:用于区分局部变量和成员变量同名的情况。使用
this.属性名称
的一定是成员变量,没有使用this.
的变量,根据就近原则来确定使用哪个变量。
-
-
示例:
-
public class Animal { private String name; private int legs; public String getName() { return name; } public int getLegs() { return legs; } public void setName(String name) { this.name = name; } public void setLegs(int legs) { if (legs >= 0 && legs % 2 == 0) { this.legs = legs; } } public void move() { System.out.println("我叫" + name + ",我用" + legs + "条腿跑步!"); } } /* * this关键字的使用 * 1.this可以用来修饰:属性、方法、构造器 * * 2.this修饰属性和方法: * this理解为当前对象或者当前正在创建的对象 * * 2.1 在类的方法中,我们可以使用"this.属性"或者"this.方法"的形式,调用当前对象的属性或者方法。 * 但是,通常情况下,我们都选择省略"this."。特殊情况下,如果方法的形参和类的属性同名时,我们必须 * 显式的使用"this.变量"的方式,表明此变量是属性,而非形参。 * * 2.2 在类的构造器中,我们可以使用"this.属性"或者"this.方法"的形式,调用当前正在创建对象的属性或者方 法。 * 但是,通常情况下,我们都选择省略"this."。特殊情况下,如果构造器的形参和类的属性同名时,我们必须 * 显式的使用"this.变量"的方式,表明此变量是属性,而非形参。 * */
-
5. 构造方法
5.1 构造方法概述
-
构造方法:构造函数,构造器,Constructor
-
作用:用于给对象中的成员变量赋值。因为在创建对象的同时,JVM会自动调用构造方法,等对象创建完成的时候,对象中的成员变量就已经有指定的值了。
-
语法结构:
-
修饰符 方法名称(参数列表) {
方法体
} -
构造方法说明:
-
构造方法的方法名称,必须和类名一模一样,连大小写都一样
-
构造方法没有返回值类型,连void也没有
-
构造方法没有return语句,如果一定需要return语句,就写一个return;return后面不能跟内容,加return没有实际的意义
-
构造方法不需要手动调用,由JVM在创建对象的时候自动调用
-
对象本身不能调用构造方法
-
构造方法只调用一次
-
-
示例:
-
public class Person { private String name; private int age; private String gender; public Person() { System.out.println("我被执行了"); } } ....... public class PersonTest { public static void main(String[] args) { Person person = new Person(); } } // 在创建对象的同时,会自动调用构造方法
注意事项:
-
构造方法可以是有参数的,也可以是没有参数的。
-
如果是没有参数的构造方法,外界无需传入任何的参数值,只能给成员变量赋固定值或者不赋值。
-
如果是有参数的构造方法,外界在调用构造方法的时候,需要传入实际的参数值,通常用于赋值给成员变量。
-
-
如果在类中没有定义任何的构造方法,那么系统会自动提供一个空参构造(空实现)。
-
如果在类中手动定义了一个构造方法(无论是空参还是有参),系统都不再会提供任何的构造方法。
-
构造方法的重载:构造方法都在同一个类中,构造方法的方法名称都和类名一致,参数列表不同,没有返回值类型,一般在类中,既需要空参构造,也需要有参构造,都手动定义出来。
-
示例:
-
public class Person { private String name; private int age; private String gender; public Person(String name, int age, String gender) { this.name = name; this.age = age; this.gender = gender; } public Person() { } 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 getGender() { return gender; } public void setGender(String gender) { this.gender = gender; } @Override public String toString() { return "Person{" + "name='" + name + '\'' + ", age=" + age + ", gender='" + gender + '\'' + '}'; } } public class PersonTest { public static void main(String[] args) { Person person = new Person("zhangsan", 18, "男"); System.out.println(person); } } /*****************************************************/ 我们也可以给set方法进行改造,让它返回对应的类型,改造方式如下: public Person setName(String name) { this.name = name; return this; } public Person setAge(int age) { this.age = age; return this; } public Person setGender(String gender) { this.gender = gender; return this; } 创建对象时可以使用链式编程的方式 Person person = new Person().setName("lisi").setAge(20).setGender("男");
5.2 构造方法和set方法的比较
-
共同点:构造方法和set方法都是用于给成员变量赋值。不希望外界直接访问私有成员变量,通过构造方法或者set方法,间接的访问私有变量。
-
区别:
-
构造方法:构造方法是在创建对象的同时,由JVM自动调用执行,用于给属性赋值,只能执行一次。
-
set方法:set方法是在创建对象之后,由对象手动调用执行,用于给属性修改值,可以调用多次。
-
-
使用场景比较:
-
构造方法:只能在创建对象的时候被自动调用一次,代码更加简洁。一旦对象创建成功,就不能继续使用构造方法修改成员变量的值。
-
set方法:一般set方法使用更加灵活,使用更加频繁。
-
-
总结:属性赋值的先后顺序
-
默认初始化
-
显示初始化
-
构造器初始化
-
通过"对象.方法"或者"对象.属性"的方式,赋值
-
最终结果由最后执行的方式决定
-
6. 静态
6.1 静态概述
-
静态:
static
关键字,静态、静止的。静态变量不会随着对象的变化而变化。 -
加载时机:
-
随着类的加载而加载。
-
静态变量随着类的加载进方法区,就直接在静态区给开辟了存储静态变量的内存空间。
-
-
静态变量优先于对象而存在。
-
静态变量被所有该类对象所共享。
-
代码层面:可以使用类名直接调用,不需要使用对象名称。在不创建对象的前提下,仍然可以使用这个静态变量。
-
强烈建议使用类名来访问。
6.2 静态效果
-
不使用静态:
-
现象:如果某个类型的所有对象,都具有一个相同的属性值,比如2个对象的country都是中国; 那么这个属性值就没有必要在所有对象中,都存储一份。
-
缺点:浪费内存空间;维护难度大,一旦需要修改,就得修改所有的对象。
-
示例:
-
public class StaticDemo { private String username; private String country; public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getCountry() { return country; } public void setCountry(String country) { this.country = country; } public static void main(String[] args) { StaticDemo st1 = new StaticDemo(); st1.setUsername("zs"); st1.setCountry("中国"); StaticDemo st2 = new StaticDemo(); st1.setUsername("lisi"); st1.setCountry("中国"); st1.setCountry("东三省"); st2.setCountry("东三省"); } }
使用静态:
-
现象:如果某个类型的所有对象,都具有一个相同的属性值,那么就在这个属性的定 义上,加一个static静态关键字。让该变量存储在方法区字节码的静态区中,避免了所有对象都存储相 同数据的问题,节省了内存空间,将来维护容易(只需要修改一次)。
-
示例:
-
public class StaticDemo { private String username; private static String country; public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public static String getCountry() { return country; } public static void setCountry(String country) { StaticDemo.country = country; } public static void main(String[] args) { StaticDemo st1 = new StaticDemo(); st1.setUsername("zs"); st1.setCountry("中国"); StaticDemo st2 = new StaticDemo(); st1.setUsername("lisi"); System.out.println(st2.getCountry()); st2.setCountry("东三省"); System.out.println(st1.getCountry()); } }
-
注意事项:
-
静态方法:在方法声明上,加上了
static
关键字的方法,就是静态方法。 -
静态方法不能访问非静态的变量:静态方法本身可以在没有创建对象的时候调用;非静态的变量只有在对象创建之后才存在。如果静态方法可以访问非静态的变量,那么就相当于在对象创建之前,就访问了对象创建之后的数据。明显不合理。
-
静态方法不能访问非静态的方法:静态方法可以在没有创建对象的时候调用;非静态的方法可以访问非静态的变量。如果静态方法可以访问非静态的方法,就相当于静态方法间接的访问了非静态的变量,和第2点矛盾。
-
静态方法中不能存在
this
关键字:this
关键字表示本类当前对象。静态方法可以在对象创建之前调用。如果静态方法可以访问this
关键字,相当于在创建对象之前,就使用了对象本身。矛盾。 -
总结:静态资源不能访问非静态资源
-
-
6.3 静态变量和非静态变量的区别
-
概念上,所属不同:
-
非静态变量属于对象。
-
静态变量属于类,类变量。
-
-
内存空间不同,存储位置不同:
-
非静态变量属于对象,所以存储在堆内存中。
-
静态变量属于类,存储在方法区的静态区中。
-
-
内存时间不同,生命周期不同:
-
非静态变量属于对象,所以生命周期和对象相同,随着对象的创建而存在,随着对象的消失而消失。
-
静态变量属于类,所以生命周期和类相同,随着类的加载。
-
-
访问方式不同:
-
非静态变量只能使用对象名访问。
-
静态变量既可以使用对象访问,也可以通过类名访问(强烈建议使用类名访问):
-
类名.静态变量名
-
类名.静态方法名()
-
-
6.4 工具类
-
工具类:在一个类中,没有维护什么数据,没有任何的成员变量,相当于是一个工具。类中就都是一些静态方法,快速的解决一些常见的问题。
-
方法都是静态的,不需要创建对象;创建对象会浪费系统资源。希望控制不让创建对象(方式:使用构造方法私有化)。
-
示例:数组工具类
-
public class ArraysUtils { // 构造方法私有化,保证类不会被创建对象 private ArraysUtils() { } // 1、循环打印数组 public static void print(int[] arr) { for (int i = 0; i < arr.length; i++) { System.out.print(arr[i] + ","); } } // 2、循环打印数组,一行打印指定个数的元素 public static void print(int[] arr, int number) { for (int i = 0; i < arr.length; i++) { System.out.print(arr[i] + ","); if ((i + 1) % number == 0) { // 换一行 System.out.println(); } } } // 3、将数组转成[元素1,元素2...]这种格式的字符串 public static String formatPrint(int[] arr) { if (arr == null) return "null"; // 如果iMax是-1 意味着数组长度是0 int iMax = arr.length - 1; if (iMax == -1) return "数组长度为0"; StringBuilder b = new StringBuilder(); b.append('[');// 在最前方追加一个[ for (int i = 0; ; i++) { b.append(arr[i]); // 满足条件 就退出 if (i == iMax) { return b.append(']').toString(); } b.append(", "); } } // 4、将数组冒泡\或者其他算法排序(直接将原数组排序) public static void sort(int[] arr) { for (int i = 0; i < arr.length - 1; i++) { for (int j = 0; j < arr.length - 1 - i; j++) { if (arr[j] > arr[j + 1]) { int desk = arr[j]; arr[j] = arr[j + 1]; arr[j + 1] = desk; } } } } // 5、将数组冒泡\或者其他算法排序(不允许排形参的数组,需要直接返回一个排好序的新数组) public static int[] sortNew(int[] arr) { int arrNew[] = new int[arr.length]; // 把源数组 内容赋值一份给 arrNew // arrNew = Arrays.copyOf(arr, arr.length); for (int i = 0; i < arrNew.length; i++) { arrNew[i] = arr[i]; } for (int i = 0; i < arrNew.length - 1; i++) { for (int j = 0; j < arrNew.length - 1 - i; j++) { if (arrNew[j] > arrNew[j + 1]) { int desk = arrNew[j]; arrNew[j] = arrNew[j + 1]; arrNew[j + 1] = desk; } } } return arrNew; } // 6、比较两个数组的所有元素是否完全一致 public static boolean isEquals(int[] arr1, int[] arr2) { // 地址相同 里面的数据肯定相同 if (arr1 == arr2) return true; if (arr1 == null || arr2 == null) return false; int length = arr1.length; if (arr2.length != length) return false; for (int i = 0; i < length; i++) if (arr1[i] != arr2[i]) return false; return true; } // 7、计算数组的平均值 public static double avg(int[] arr) { double sum = 0; for (int i = 0; i < arr.length; i++) { sum += arr[i]; } return sum / arr.length; } // 8、计算数组的最大值 public static int max(int[] arr) { // 数组第一个 为最大值 int maxnumber = arr[0]; for (int i = 1; i < arr.length; i++) { if (maxnumber < arr[i]) { maxnumber = arr[i]; } } return maxnumber; } // 9、计算数组的最小值 public static int min(int[] arr) { // 数组第一个 为最小值 int minnumber = arr[0]; for (int i = 0; i < arr.length; i++) { if (minnumber > arr[i]) { minnumber = arr[i]; } } return minnumber; } // 10、将一个数组的所有元素都反转(比如{5,4,8}变成{8,4,5}) public static int[] reverse(int[] arr) { int[] arrNew = new int[arr.length]; for (int x = 0; x < arr.length; x++) { arrNew[x] = arr[arr.length - 1 - x]; } return arrNew; } // 11、判断一个指定的数值在数组中是否存在 public static boolean isExits(int[] arr, int number) { boolean isTrue = false; for (int i = 0; i < arr.length; i++) { if (arr[i] == number) { return true; } } // 如果不存在 return false; } }
7. 代码块
7.1 概述
-
使用大括号包起来的一段代码。放在不同的位置,有不同的名称,有不同的作用,有不同的执行时机。
-
分类:
-
局部代码块
-
构造代码块
-
静态代码块
-
同步代码块(多线程中会遇到)
-
7.2 局部代码块
-
格式:使用大括号包起来的一段代码
-
位置:方法中
-
作用:
-
限定变量的生命周期,作用域只是在大括号内部。
-
在局部代码块中【声明】的变量,只能在局部代码块的范围内使用,一旦出了局部代码块的大括号,变量就不能继续使用了。
-
某个变量一旦不能使用了,就会被回收,节省内存空间。
-
-
注意事项:
-
局部代码块内声明的变量,会减少变量的生命周期,局部代码块执行完毕后,就无法继续使用变量。
-
局部代码块外声明的变量,局部代码块执行完毕后,仍可以继续使用变量。
-
-
示例:
-
public class Demo { public static void main(String[] args) { int a = 123; { int b = 456; System.out.println(a); System.out.println(b); } System.out.println(a); } }
7.3 构造代码块
-
格式:使用大括号包起来的一段代码
-
位置:类中方法外
-
作用:
-
用于给成员变量初始化赋值。
-
在创建对象时执行部分操作。
-
-
构造代码块的执行说明:
-
在创建对象的时候执行,由 JVM 默认调用,用于给对象的属性赋值。
-
在构造方法执行之前执行。
-
任意一个构造方法执行之前,都会执行一次构造代码块的内容。
-
如果每个构造方法都会执行的内容,请提取到构造代码块中执行。
-
-
示例:
-
public class Demo { { System.out.println("我是一个构造代码块"); } public static void main(String[] args) { Demo demo = new Demo(); } }
7.4 静态代码块
-
格式:
-
static {
静态代码块的内容
} -
位置:类中方法外
-
作用:
-
用于给静态的成员变量初始化赋值
-
用于执行那些只需要执行一次的代码,例如驱动加载等
-
-
执行特点:
-
随着类的加载而执行
-
类只加载一次,所以静态代码块只执行一次
-
执行的时机最早:早于所有的对象相关内容,比main方法执行的早
-
-
示例:
-
public class Demo { static { System.out.println("说我是静态代码块"); } { System.out.println("我是构造代码块"); } public static void main(String[] args) { System.out.println("我是main方法"); Demo demo = new Demo(); } }