面向对象
类是对象的抽象,而对象是类的具体实例。类和对象好比图纸和实物的关系,模具和铸件的关系。
1 类
类的成员包含:字段、方法、代码块、内部类、接口等。
定义一个类时的权限修饰符规则:
(1) 外部类:public/ (default)
(2) 成员内部类: public /protected /(default)/ private
(3) 局部内部类:不能有权限修饰符
(1)字段(成员变量)和方法
在类中,但是在方法外部定义的变量,用于描述一个类中包含哪些数据。
public class Student {
String name; //成员变量直接定义在类中
int age;
public void eat() { //成员方法不要写static关键字
System.out.println(“hello”);
}
}
(2) static关键字
static 用来修饰属性、方法、代码块、类。 static的主要意义是在于创建独立于具体对象的域变量或者方法。方便在没有创建对象时进行调用(方法/变量)
1) 修饰属性
- Java静态属性和类相关,和具体的实例无关
- 访问方式:
类名.属性
class Main{
static int a=3;
public static void main(String[] args){
Main.a;//直接通过类名.属性的方式访问。
}
}
2) 修饰方法
- 静态方法属于类,不属于类的对象
- 可以直接调用静态方法,
类名.静态方法()
- 静态方法可以访问静态数据成员,并可以更改静态成员的值
static方法
- 静态方法不能使用非静态数据成员或调用非静态方法,因为非静态成员变量和非静态成员方法必须依赖于具体的对象才能被调用。但在非静态成员方法中是可以访问静态成员方法和静态成员变量的。
- this是当前实例的引用,super是当前父类实例的引用,故不能应用于静态上下文中
static变量
- 静态变量被所有对象共享,在内存中只有一个副本,在类初次加载的时候才会被初始化
- 非静态变量是对象所拥有的,在创建对象的时候被初始化,存在多个副本,各个对象拥有的副本互不影响
static块 (用来提升程序性能)
- 静态代码块用于类的初始化操作 ,在静态代码块中不能直接访问非static成员
- 类中可以有多个静态代码块(方法中不能有静态代码块),在类初次加载时执行,并且只会执行一次。
为什么说static块可以用来优化程序性能?
是因为static块在类加载的时候执行一次。因此,很多时候会将一些只需要进行一次的初始化操作都放在static代码块中进行。
public class Main {
static{
System.out.println("hello");
}
public void say(){
{
System.out.println("hello");
}
}
}
注意:
- 静态成员变量虽然独立于对象,但是不代表不可以通过对象去访问,所有的静态方法和静态变量都可以通过对象访问(只要访问权限满足就好),比如this代表当前对象的引用,那么就可以用this去访问静态变量或方法。
- static不允许用来修饰局部变量(语法规定)
public void function(){
static int i;//编译报错
}
补充: 静态导入
import static
可以导入包中的静态方法和字段。
比如下面的System.out.println()
就省略了System
; Math.sqrt(Math.pow(2,2))
就省略了Math
。
import static java.lang.System.*;
import static java.lang.Math.*;
public static void main(String[] args){
out.println("hello world");
out.println(sqrt(pow(2,2)));
}
2 类的使用:
(1) 导包:指出所需类的位置
import 包名称.类名称
对于当前类属于同一个包时,可以省略包语句
常见的系统包:
java.lang:
系统常用基础类(String、Object),此包从JDK1.1后自动导入。java.lang.reflect:java
反射编程包;java.net:
进行网络编程开发包。java.sql:
进行数据库开发的支持包。java.util:
是java提供的工具程序包。(集合类等) 非常重要java.io
:I/O编程开发包。
(2)创建
类名称 对象名 = new 类名称();
Student stu = new Student();
(3)使用
对象名.成员变量名
对象名.成员方法名(参数)
注意:
如果成员变量没有进行赋值,那么stu.name将会得到一个默认值
默认值规则(同数组):
- 对于各种数字类型,默认值为0;
- 对于boolean类型,默认值为false;
- 对于引用类型(String,Array…),默认值为null
(4 )示例
//定义学生类
public class Student {
String name;
int age;
public void eat() {
System.out.println(“hello”);
}
}
//将对象名作为参数传递
public class Demo_test{
public static void main(String[] args){
Student one = new Student();
one.name = “ROck”;
method(one); //传进去的参数其实是地址值
}
public static void method(Student param){
System.out.println(param.name);
}
}
//使用对象类型作为方法的返回值
public class Demo_test01{
public static void main(String[] args){
Student one = getStudent();
System.out.println(one.name);
}
public static Student getStudent(){
Student one = new Student();
one.name = “ROck”;
return one; //返回的为地址值
}
}
(5) 成员变量和局部变量的区别
1)定义的位置不同(重点)
局部变量:在方法的内部
成员变量:在方法的外部,直接写在类中
2)作用的范围不一样(重点)
局部变量:只有在方法中才能使用
成员变量:整个类全部都可以使用
3)默认值不一样(重点)
局部变量:没有默认值,必须手动进行赋值
成员变量:没有赋值,会有默认值,规则和数组一样
4)内存的位置不一样
局部变量:位于栈内存
成员变量:位于堆内存
5)生命周期不一样
局部变量:随着方法进栈而诞生,随着方法出栈而消失
成员变量:随着对象的创建而诞生,随着对象被垃圾回收而消失
注意:
创建一个对象的时候,到底是在栈中分配还是在堆中分配需要看2个方面:对象类型和存在的位置
- 如果是基本数据类型,byte、short、int、long、float、double、char,在方法中声明时,则存储在栈中;其它情况都是在堆中(比方说类的成员变量就在堆中);
- 除了基本数据类型之外的对象,JVM会在堆中创建对象,对象的引用存于虚拟机栈中的局部变量表中
- 并不是所有的对象都在堆中存储,可以走栈上分配,在不在栈上分配取决于Hotspot的一个优化技术:“逃逸分析” 一般JVM 执行某个方法的频次比较高的时候 才会触发逃逸分析,如果不满足方法逃逸就会在栈上分配。
参考博客:https://blog.csdn.net/zhangshuny/article/details/107514559
封装
面向对象的三大特征:封装、继承、多态
封装:隐藏对象的属性和实现细节,仅对外提供公共访问方式。即将类中的属性或者方法用private
关键字进行修饰,以达到在类外部不能访问的需求,同时提供一系列的get和set的方法。
封装的优点:让类的调用者不必太多的了解类是如何实现的。降低了代码的复杂度。
1 方法本身就是一种封装
2 关键字private也是一种封装
被private修饰的成员变量或成员方法,不能直接被类的调用者使用,使用private封装成员属性,提供public方法供类的调用者使用,让调用者间接访问private成员变量。
public class Person{
String name;
private int age;
public void show(){
System.out.println(“我叫:”+ name +“,年龄”+ age);
}
}
3 getter和setter方法
public class Person{
String name;
private int age;
public void show(){
System.out.println(“我叫:”+ name +“,年龄”+ age);
}
//这个成员方法,专门用来向age设置数据
public void setAge(int num){
if(num<100 && num >=9){
age =num;
} else {
System.out.println(“数据不合理”);
}
}
//专门用来获取age数据
public int getAge(){
return age;
}
}
注意:
- 间接访问private成员变量,就是定义一对Getter/Setter方法 必须叫setXxxx或getXxxx
- 对于Getter来说,不能有参数,返回值类型和成员变量类型对应
- 对于Setter来说,不能有返回值,参数类型和成员变量类型对应
通过设置方法间接访问变量,在方法中设置筛选规则,提高代码的安全性
示例
class Person{
private String name;
private int age;
public void setName(String name){
this.name = name;//this表示调用该方法的对象
}
public String getName(){
return name;
}
public void show(){
System.out.println("name:"+name);
}
public static void main(String[] args){
Person person = new Person();
person.setName("Tco");
String name = person.getName();
System.out.println(name);
Person.show();
}
}
当set方法的形参名字和类中成员属性的名字一样时,应使用当前实例的引用。
注意:
通常将类的成员变量称为字段,当设置了setter/getter方法后,我们可以称之为属性
4 this
当方法的局部变量和类的成员变量重名的时候,根据“就近原则”,优先使用局部变量
如果需要访问本类当中的成员变量,需要使用格式:
this.成员变量
//this为所在类当前对象的引用(找到对象的线索)
public class Person{
String name;
public void sayHello(String name){
System.out.println(name +"你好,我是:"+this.name);
}
}
public class DemoTest{
public static void main(String[] args){
Person person = new Person();
person.name = "rock";
person.sayHello("cbj");
}
}
5 构造方法
new执行过程(对象的产生):
- 为对象分配内存空间
- 调用对象的构造方法
当用new来创建对象时,就是在调用构造方法。
public 类名称(参数类型 参数名称){
方法体
}
注意:
(1)构造方法的名称必须和所在类名称一样,普通方法也可以与类名称相同(不建议这么做)
(2)不能有返回值,void也不能写
(3)不能return一个具体的返回值
(4)编译器有默认的构造方法
(5)可以编写多个构造方法,构造方法可以进行重载
(6)若定义了构造方法,默认的无参构造将不会生成。
class Person{
private String name;
private int age;
private String sex;
//无参数构造函数
public Person(){
this.name = "rock";
this.age =10;
this.sex = "男";
}
//带有3个参数的构造函数
public Person(String name,int age,String sex){
this.name = name;
this.age =age;
this.sex = sex;
}
public void show(){
System.out.println("name:"+name+"age:"+age+"sex:"+sex);
}
public class Main{
public static void main(String[] args){
//调用无参构造
Person p1 = new Person();
p1.show();
//调用有参构造函数
Person p2 = new Person("TKK",80,"男");
p2.show();
}
}
}
构造器(constructor)是否可被重写(override)?
构造器不能被继承,因此不能被重写,但可以被重载。
继承条件下构造方法的调用规则如下:
情况1:如果子类的构造方法中没有通过super显式调用父类的有参构造方法,也没有通过this显式调用自身的其他构造方法,则系统会默认先调用父类的无参构造方法。
情况2:如果子类的构造方法中通过super显式调用父类的有参构造方法,那将执行父类相应构造方法,而不执行父类无参构造方法。
情况3:如果子类的构造方法中通过this显式调用自身的其他构造方法,在相应构造方法中应用以上两条规则。
特别注意的是,如果存在多级继承关系,在创建一个子类对象时,以上规则会多次向更高一级父类应用,一直到执行顶级父类Object类的无参构造方法为止。
6 构造一个完整的类
标准的类的四个组成部分:
1.所有的成员变量都要使用private关键字修饰
2.为每一个成员变量编写一对Getter/Setter方法
3.编写一个无参数的构造方法
4.编写一个全参数的构造方法
public class Student {
private String name;
private int age;
//无参数构造方法
public Student() {
}
//有参数构造方法
public Student(String name, int age) {
this.name = name;
this.age = age;
}
//Getter/Setter
public String setName(String name){
this.name = name;
}
public String getName(){
return name;
}
public String setAge(String age){
this.age = age;
}
public String getAge(){
return age;
}
}
//使用
public class DemoStudent {
public static void main(String[] args) {
Student stu1 = new Student();
stu1.setName(“tock”);
stu1.setAge(42);
System.out.println(“姓名”+ stu1.getName() + “年龄”+stu1.getAge());
System.out.println(“====================”);
Student stu2 = new Student(“rock”,21);
System.out.println(“姓名”+ stu2.getName() + “年龄”+stu2.getAge());
stu2.setAge(25);
System.out.println(“姓名”+ stu2.getName() + “年龄”+stu2.getAge());
}
}
7 代码块
字段(成员变量)的初始化方法:
- 就地初始化
- 使用构造方法初始化
- 使用代码块初始化
代码块:
- 普通代码块
- 构造块
- 静态块
- 同步代码块
(1)普通代码块
定义在方法中的代码块
public class Main{
public static void main(String[] args){
{ //普通代码块,直接定义在方法中
int x=10;
System.out.println("x1="+x); //10
}
int x=100;
System.out.println("x2="+x); //100
}
}
(2)构造代码块
定义在类中的代码块(实例代码块)
用于初始化实例成员变量
class Person{
private String name;
private int age;
public Person(){
System.out.println("Person init");
}
//实例代码块
{
this.name = "rock";
this.age = 53;
System.out.println("instance init");
}
public void show(){
System.out.println("name:"+name+",age:"+age);
}
}
//使用
public class Main{
public static void main(String[] args){
Person p1 = new Person();
p1.show();//name:rock,age:53
}
}
//instance init
//Person init
//name:rock,age:53
实例代码块先于构造函数执行
(3)静态代码块
使用static定义的代码块
用于初始化静态成员变量
class Person{
private String name;//实例成员变量
private int age;
private static int count =0;//静态成员变量
public Person(){
System.out.println("Person init");
}
//实例代码块
{
this.name = "rock";
this.age = 53;
System.out.println("instance init");
}
//静态代码块
static{
count = 10;//只能访问静态数据成员
System.out.println("static init");
}
public void show(){
System.out.println("name:"+name+",age:"+age);
}
}
//使用
public class Main{
public static void main(String[] args){
Person p1 = new Person();
Person p2 = new Person();//当第二次创建对象时静态代码块不会执行
p1.show();//name:rock,age:53
}
}
注意:
- 静态代码块只在第一次创建对象时执行
- 执行顺序:静态代码块<<实例代码块<<构造函数
7 匿名对象
匿名表示没有名字的对象
- 没有引用的对象称为匿名对象
- 只能在创建对象时使用
- 应用于一个对象只想使用一次的情形
class Person{
private String name;//实例成员变量
private int age;
public Person(String name,int age){
this.name = name;
this.age = age;
}
public void show(){
System.out.println("name:"+name+",age:"+age);
}
}
//使用
public class Main{
public static void main(String[] args){
//通过匿名对象调用方法
new Person("rock",32).show();
}
}
//name:rock,age:32
8 小结
- 被static修饰的属性称为类属性,static修饰的方法称为类方法,不依赖于对象,只能通过类名进行调用
- this关键字表示当前对象的引用
- 静态代码块<<实例代码块<<构造函数