目录
目录
1.封装
1.1 封装的概念
面向对象程序三大特性:封装、继承、多态。而类和对象阶段,主要研究的就是封装特性。何为封装呢?简单来说就是套壳屏蔽细节。
比如:对于电脑这样一个复杂的设备,提供给用户的就只是:开关机、通过键盘输入,显示器,USB插孔等,让用户来和计算机进行交互,完成日常事务。但实际上:电脑真正工作的却是CPU、显卡、内存等一些硬件元件。
对于计算机使用者而言,不用关心内部核心部件,比如主板上线路是如何布局的,CPU内部是如何设计的等,用户只需要知道,怎么开机、怎么通过键盘和鼠标与计算机进行交互即可。因此计算机厂商在出厂时,在外部套上壳子,将内部实现细节隐藏起来,仅仅对外提供开关机、鼠标以及键盘插孔等,让用户可以与计算机进行交互即可。封装:将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节,仅对外公开接口来和对象进行交互.
1.2 访问限定符
Java中主要通过类和访问权限来实现封装:类可以将数据以及封装数据的方法结合在一起,更符合人类对事物的认知,而访问权限用来控制方法或者字段能否直接在类外使用。Java中提供了四种访问限定符:
public修饰的 可以理解成所有的类都能访问,是公开的.
defalut修饰的 就是什么都不写的包默认权限,这可以在同一个包中访问
private修饰的 只可以在当前类中访问
protected修饰的 要在继承中才可以了解,后续我会更新
首先先来看public, 在Test类中可以访问Student类中的属性,完全可以,编译器不会报错.
class Student {
public String name;
public int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
}
public class Test {
public static void main(String[] args) {
Student student = new Student("xiaobai", 10);
System.out.println(student.name);
System.out.println(student.age);
}
}
再看private,在Test类中不能够访问Student类中的属性.
设计者不想让别人看到类的实现细节,但是类外的人又想使用这些属性,那该怎么办呢? 可以提供这些属性的接口供别人使用. 我们可以使用IDEA帮我们自动生成.
有了接口,我们就可以这样使用.
class Student {
private String name;
private int 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 Student() {
}
}
public class Test {
public static void main(String[] args) {
Student student = new Student();
student.setName("xiaobai");
System.out.println(student.getName());
student.setAge(10);
System.out.println(student.getAge());
}
}
1.3 封装扩展之包
要想理解明白default,就要先理解包的概念.
1.3.1 包的概念
在面向对象体系中,提出了一个软件包的概念,即:为了更好的管理类,把多个类收集在一起成为一组,称为软件包。有点类似于目录。比如:为了更好的管理电脑中的歌曲,一种好的方式就是将相同属性的歌曲放在相同文件下,也可以对某个文件夹下的音乐进行更详细的分类。
在Java中也引入了包,包是对类、接口等的封装机制的体现,是一种对类或者接口等的很好的组织方式,比如:一个包中的类不想被其他包中的类使用。包还有一个重要的作用:在同一个工程中允许存在相同名称的类,只要处在不同的包中即可。
1.3.2 导入包中的类
Java 中已经提供了很多现成的类供我们使用. 例如Date类:可以使用java.util.Date导入java.util这个包中的Date类
public class Test {
public static void main(String[] args) {
java.util.Date date = new java.util.Date();
//得到一个毫秒级别的时间戳
System.out.println(date.getTime());
}
}
但是这种写法比较麻烦一些, 可以使用 import语句导入包
import java.util.Date;
public class Test {
public static void main(String[] args) {
Date date = new Date();
//得到一个毫秒级别的时间戳
System.out.println(date.getTime());
}
}
如果需要使用java.util中的其他类, 可以使用import java.util.* *号是通配符,可以充当任何类,不是导入util下的所有类,而是用到哪个类,它就充当哪个类.
import java.util.*;
public class Test {
public static void main(String[] args) {
Date date = new Date();
//得到一个毫秒级别的时间戳
System.out.println(date.getTime());
}
}
但是我们更建议显式的指定要导入的类名. 否则还是容易出现冲突的情况
importjava.util.*;
importjava.sql.*;
public class Test {
public static void main(String[]args) {
//util和sql中都存在一个Date这样的类,此时就会出现歧义,编译出错
Date date = new Date();
System.out.println(date.getTime());
}
}
//编译出错Error:(5,9)java: 对Date的引用不明确
java.sql中的类java.sql.Date和java.util中的类java.util.Date都匹配
在这种情况下需要使用完整的类名
可以使用import static导入包中静态的字段和方法
import static java.lang.Math.*;
public class Test {
public static void main(String[] args) {
double x = 30;
double y = 40;
//静态导入的方式写起来更方便一些.
// double result = Math.sqrt(Math.pow(x,2)+Math.pow(y,2));
double result = sqrt((pow(x, 2)) + pow(y, 2));
System.out.println(result);
}
}
1.3.3 自定义包
基本规则
在文件的最上方加上一个 package 语句指定该代码在哪个包中.
包名需要尽量指定成唯一的名字, 通常会用公司的域名的颠倒形式(例如com.baidu.com)
包名要和代码路径相匹配. 例如创建com.bit.的包, 那么会存在一个对应的路径com/bit/来存储代码.
如果一个类没有 package 语句, 则该类被放到一个默认包中.
操作步骤
1. 在 IDEA 中先新建一个包: 右键 src -> 新建 -> 包2. 在弹出的对话框中输入包名, 例如com.bit. 在包中创建类, 右键包名 -> 新建 -> 类, 然后输入类名即可.
2. 在弹出的对话框中输入包名, 例如com.bit
3. 在包中创建类, 右键包名 -> 新建 -> 类, 然后输入类名即可
4. 此时可以看到我们的磁盘上的目录结构已经被 IDEA 自动创建出来了.
5. 同时我们也看到了, 在新创建的Test.java文件的最上方, 就出现了一个 package 语句
注意: package是声明当前java文件是在哪个包中,还有包名一定要都是小写.
1.3.4 包的访问权限控制举例
我在bit这个包里面创建了两个类
首先,现在TestBit1这个类里面,age的访问修饰符就是default,default就是什么都不写.
在TestBit2这个类里面 实例化TestBit1类的一个对象,然后用testBit1这个引用去访问age,发现没有报错,这就是包访问权限,也就是default.
1.3.5 常见的包
1. java.lang:系统常用基础类(String、Object),此包从JDK1.1后自动导入。
2. java.lang.reflect:java 反射编程包;
3. java.net:进行网络编程开发包。
4. java.sql:进行数据库开发的支持包。
5. java.util:是java提供的工具程序包。(集合类等) 非常重要
6. java.io:I/O编程开发包。
2. static成员
2.1 static修饰成员变量
class Student {
public String name;
public String gender;
public int age;
public double score;
public static String classRoom = "301";
public Student(String name, String gender, int age, double score) {
this.name = name;
this.gender = gender;
this.age = age;
this.score = score;
}
}
public class Test {
public static void main(String[] args) {
Student students1 = new Student("Lileilei","男",18,3.8);
Student students2 = new Student("HanMeiMei","女",19,4.0);
Student students3 = new Student("Jim","男",18,2.6);
System.out.println(Student.classRoom);
}
}
假设三个同学是同一个班的,那么他们上课肯定是在同一个教室,那既然在同一个教室,那能否给类中再加一个成员变量,来保存同学上课时的教室呢?答案是不行的。
在Student类中定义的成员变量,每个对象中都会包含一份(称之为实例变量),因为需要使用这些信息来描述具体的学生。而现在要表示学生上课的教室,这个教室的属性并不需要每个学生对象中都存储一份,而是需要让所有的学生来共享。在Java中,被static修饰的成员,称之为静态成员,也可以称为类成员,其不属于某个具体的对象,是所有对象所共享的。
static修饰的成员变量,称为静态成员变量,静态成员变量最大的特性:不属于某个具体的对象,是所有对象所共享的。
【静态成员变量特性】
1. 不属于某个具体的对象,是类的属性,所有对象共享的,不存储在某个对象的空间中
2. 既可以通过对象访问,也可以通过类名访问,但一般更推荐使用类名访问
3. 类变量存储在方法区当中
4. 生命周期伴随类的一生(即:随类的加载而创建,随类的卸载而销毁)
那么我们就可以总结一下
普通成员变量: 定义在类的内部,方法的外部.
静态成员变量: 定义在类的内部,方法的外部,但是被static修饰了
局部变量: 定义在方法的内部
2.2 static修饰成员方法
一般类中的数据成员都设置为private,而成员方法设置为public,那设置之后,Student类中classRoom属性如何在类外访问呢?
class Student {
private String name;
private String gender;
private int age;
private double score;
private static String classRoom = "301";
public Student(String name, String gender, int age, double score) {
this.name = name;
this.gender = gender;
this.age = age;
this.score = score;
}
}
public class Test {
public static void main(String[] args) {
Student students1 = new Student("Lileilei","男",18,3.8);
Student students2 = new Student("HanMeiMei","女",19,4.0);
Student students3 = new Student("Jim","男",18,2.6);
System.out.println(Student.classRoom);
}
}
可以看到,classRoom不能被访问,报错了.
那static属性应该如何访问呢?
Java中,被static修饰的成员方法称为静态成员方法,是类的方法,不是某个对象所特有的。静态成员一般是通过静态方法来访问的.
class Student {
private String name;
private String gender;
private int age;
private double score;
private static String classRoom = "301";
public static String getClassRoom() {
return classRoom;
}
public Student(String name, String gender, int age, double score) {
this.name = name;
this.gender = gender;
this.age = age;
this.score = score;
}
}
public class Test {
public static void main(String[] args) {
System.out.println(Student.getClassRoom());
}
}
【静态方法特性】
1. 不属于某个具体的对象,是类方法
2. 可以通过对象调用,也可以通过类名.静态方法名(...)方式调用,更推荐使用后者
3. 不能在静态方法中访问任何非静态成员变量
4. 静态方法中不能调用任何非静态方法,因为非静态方法有this参数,在静态方法中调用时候无法传递this引用
2.3 static成员变量初始化
注意:静态成员变量一般不会放在构造方法中来初始化,构造方法中初始化的是与对象相关的实例属性
静态成员变量的初始化分为两种:就地初始化和静态代码块初始化。
1. 就地初始化指的是:在定义时直接给出初始值
class Student {
private String name;
private int age;
private static String classRoom = "301";
}
2. 静态代码块初始化
那什么是代码块呢?继续往后看 :) ~~~
3. 代码块
3.1 代码块概念以及分类
使用{}定义的一段代码称为代码块。根据代码块定义的位置以及关键字,
又可分为以下四种:
普通代码块
构造块
静态块
同步代码块(后续讲解多线程部分再谈)
3.2 普通代码块
普通代码块:定义在方法中的代码块.
public class Main {
public static void main(String[]args) {
{
//直接使用{}定义,普通方法块
int x = 10;
System.out.println("x1="+x);
}
int x = 100;
System.out.println("x2="+x);
}
}
3.3 构造代码块
构造块:定义在类中的代码块(不加修饰符)。也叫:实例代码块。构造代码块一般用于初始化实例成员变量。
class Student {
private String name;
private int age;
private static String classRoom = "301";
public Student() {
System.out.println("不带参数的构造方法!");
}
// 实例(构造)代码块
{
this.name = "zhang san";
this.age = 18;
}
public void show() {
System.out.println(this.name + " 年龄是: "+ this.age+ " 他正在上课!");
}
}
public class Test {
public static void main(String[] args) {
Student student = new Student();
student.show();
}
}
3.4 静态代码块
使用static定义的代码块称为静态代码块。一般用于初始化静态成员变量
class Student {
private String name;
private int age;
public static String classRoom;
public Student() {
System.out.println("不带参数的构造方法!");
}
// 实例(构造)代码块
{
this.name = "zhang san";
this.age = 18;
}
// 静态代码块
static {
classRoom = "301";
}
public void show() {
System.out.println(this.name + " 年龄是: "+ this.age+ " 他正在上课!");
}
}
public class Test {
public static void main(String[] args) {
Student student = new Student();
student.show();
System.out.println("他所在的班级是:" + Student.classRoom);
}
}
注意事项
静态代码块不管生成多少个对象,其只会执行一次
静态成员变量是类的属性,因此是在JVM加载类时开辟空间并初始化的
如果一个类中包含多个静态代码块,在编译代码时,编译器会按照定义的先后次序依次执行(合并)
实例代码块只有在创建对象时才会执行
4. 对象的打印
public class Test {
public static void main(String[] args) {
// Student student = new Student("zhangsan", 18);
System.out.println(Student.classRoom);
}
}
public class Person {
String name;
String gender;
int age;
public Person(String name, String gender, int age) {
this.name = name;
this.gender = gender;
this.age = age;
// 如果想要默认打印对象中的属性该如何处理呢?答案:重写toString方法即可。
}
public static void main(String[] args) {
Person person = new Person("Jim","男", 18);
System.out.println(person);
}
如果想要默认打印对象中的属性该如何处理呢?答案:重写toString方法即可。
public class Person {
String name;
String gender;
int age;
public Person(String name, String gender, int age) {
this.name = name;
this.gender = gender;
this.age = age;
}
@Override
public String toString() {
return "[" + name + "," + gender + "," + age + "]";
}
public static void main(String[] args) {
Person person = new Person("Jim","男", 18);
System.out.println(person);
}
}
5.内部类
当一个事物的内部,还有一个部分需要一个完整的结构进行描述,而这个内部的完整的结构又只为外部事物提供服 务,那么这个内部的完整结构最好使用内部类。在 Java 中,可以将一个类定义在另一个类或者一个方法的内部, 前者称为内部类,后者称为外部类。内部类也是封装的一种体现。
public class OutClass {
class InnerClass{
}
}
// OutClass是外部类
// InnerClass是内部类
【注意事项】
1. 定义在class 类名{}花括号外部的,即使是在一个文件里,都不能称为内部类
public class A{
}
class B{
}
// A 和 B是两个独立的类,彼此之前没有关系
2. 内部类和外部类共用同一个java源文件,但是经过编译之后,内部类会形成单独的字节码文件
5.1 内部类
在外部类中,内部类定义位置与外部类成员所处的位置相同,因此称为成员内部类。
5.1.1 实例内部类
即未被static修饰的成员内部类。
class OuterClass {
public int data1 = 10;
private int data2 = 20;
public static int data3 = 30;
class InnerClass {
public int data1 = 1111;
public int data4 = 40;
private int data5 = 50;
public static final int data6 = 60;//final修饰的常量 常量是在编译的时候就已经确定了
public void testInner() {
System.out.println("testInner");
//实例内部类 是存在外部类的this的
System.out.println(OuterClass.this.data1);
System.out.println(this.data1);
System.out.println(data2);
System.out.println(data3);
System.out.println(data4);
System.out.println(data5);
System.out.println(data6);
}
}
public void testOuter() {
InnerClass innerClass = new InnerClass();
System.out.println(innerClass.data1);
}
}
public class Test {
public static void main(String[] args) {
OuterClass outerClass = new OuterClass();
OuterClass.InnerClass innerClass = outerClass.new InnerClass();
OuterClass.InnerClass innerClass2 = new OuterClass().new InnerClass();
innerClass2.testInner();
outerClass.testOuter();
}
}
【注意事项】
1. 外部类中的任何成员都可以在实例内部类方法中直接访问
2. 实例内部类所处的位置与外部类成员地位相同,因此也受public、private等访问限定符的约束
3. 在实例内部类方法中访问同名的成员时,优先访问自己的,如果要访问外部类同名的成员,必须:外部类名 称.this.同名成员 来访问
4. 实例内部类对象必须在先有外部类对象前提下才能创建
5. 实例内部类的非静态方法中包含了一个指向外部类对象的引用
6. 外部类中,不能直接访问实例内部类中的成员,如果要访问必须先要创建内部类的对象。
5.1.2 静态内部类
pclass OuterClass2 {
public int data1 = 10;
private int data2 = 20;
public static int data3 = 30;
/**
* 1. 如何实例化静态内部类对象
*
* 2. 如何在静态内部类当中访问外部类的非静态成员??
*
*/
static class InnerClass2 {
public int data4 = 40;
private int data5 = 50;
public static int data6 = 60;
public void testInner() {
System.out.println("testInner()");
OuterClass2 outerClass2 = new OuterClass2();
System.out.println(outerClass2.data1);
System.out.println(outerClass2.data2);
System.out.println(data3);
System.out.println(data4);
System.out.println(data5);
System.out.println(data6);
}
}
}
public class Test {
public static void main(String[] args) {
OuterClass2.InnerClass2 innerClass = new OuterClass2.InnerClass2();
innerClass.testInner();
}
}
【注意事项】
1. 在静态内部类中只能访问外部类中的静态成员 如果想访问外部类中的非静态成员,我们该如何做?
答: 在静态内部类内部创建一个外部类对象就可以了.
2. 创建静态内部类对象时,不需要先创建外部类对象
5.2 局部内部类
定义在外部类的方法体或者{}中,该种内部类只能在其定义的位置使用,一般使用的非常少,此处简单了解下语法 格式。
public class OutClass {
int a = 10;
public void method(){
int b = 10;
// 局部内部类:定义在方法体内部
// 不能被public、static等访问限定符修饰
class InnerClass{
public void methodInnerClass(){
System.out.println(a);
System.out.println(b);
}
}
// 只能在该方法体内部使用,其他位置都不能用
InnerClass innerClass = new InnerClass();
innerClass.methodInnerClass();
}
public static void main(String[] args) {
// OutClass.InnerClass innerClass = null; 编译失败
}
}
【注意事项】
1. 局部内部类只能在所定义的方法体内部使用,局部内部类实例化也只能在其方法体内部实例化
2. 不能被public、static等修饰符修饰
3. 局部内部类也有自己独立的字节码文件,命名格式:外部类名字$内部类名字.class
4. 几乎不会使用
5.3 匿名内部类
interface Shape {
void draw();
}
class Student implements Comparable<Student>{
@Override
public int compareTo(Student o) {
return 0;
}
}
public class Test {
public static void main(String[] args) {
// 向上转型
Comparable<Student> comparable = new Student();
// 这就是一个匿名内部类
// new 接口是不能够实现的
// 匿名内部类就相当于 一个类实现了这个接口 并重写了方法 具体是哪个类也不清楚
/*new Comparable<Student>() {
@Override
public int compareTo(Student o) {
return 0;
}
};
*/
int a = 10;
new Shape(){
@Override
public void draw() {
// a = 20; 此时就会报错
// 在匿名内部类当中,访问的变量是不可以改变的.
System.out.println("矩形!" + a);
}
}.draw(); // 调用方法要用这种形式
}
}