Java 是面向对象的程序设计语言,类是面向对象的重要内容,可以把了当成一种自定义类型。可以使用类来定义变量,这种类型的变量统称为引用变量。
static 是一个特殊的关键字,它可用于修饰方法,成员变量等成员。static 修饰的成员表明他属于这个类本省,而不属于该类的单个实例,因为通常把 static 修饰的成员变量和 方法 也成为类变量,类方法。不使用 static 修饰的普通方法,成员变量则属于该类的单个实例,而不属于该类。因为通常把不使用 static修饰的成员变量和方法也成为实例变量,实例方法。
Java 提供了一个this 关键字,this关键字总是指向调用该方法的对象,更具 this 出现位置的不同,this作为对象的默认引用有两种情形。
this关键字最大的作用就是让类中一个方法,访问该类里的另一个方法或实例变量。 this可以代表任何对象,当this出现在某个方法体重是,它所代表的对象是不确定的,但它的类型是确定的,他所代表的对象 只能是当前类;只有当这个方法被调用时,它所代表的对象才被确定下来,谁在调用这个方法,this就代表谁。 大部分时候,一个方法访问该类中定义的其他方法,成员变量时加不加this前缀的效果是完全一样的。但是对于static 修饰的方法而言,则可以使用类来直接调用该方法,如果在 static 修饰的方法中使用 this关键字,则这个关键字就无法指向合适的对象。所以,satatic 修饰的方法不能使用 this引用,所以static 修饰的方法不能访问不使用 static 修饰的普通成员,因此 Java 语法规定: 静态成员不能直接访问非静态成员。 也可以将 this 作为返回值,如果某个方法把 this 作为返回值,则可以多次连续调用同一个方法,从而使得代码更加简洁。但是,这种把 this作为返回值的方法可能造成实际意义上的模糊。
public class MyClass {
private static int age;
public static void main(String[] args) {
new MyClass().vo().vo().vo();
}
private MyClass vo() {
++age;
System.out.println(age);
return this;
}
}
static 是一个特殊的关键字,它可用于修饰方法,成员变量等成员。static 修饰的成员表明他属于这个类本身,而不属于该类的单个实例。而我们非static 修饰的变量它属于的是实例的变量,所以stati成员不能直接
访问静态成员
因为Java里的方法不能独立存在,他必须属于一个类或一个对象,因此方法不能像函数那样被独立执行,执行方法时必须使用类或对象来作为调用者,同一个类的一个方法调用另外一个方法时,如果被调方法时普通方法,则使用默认使用 this 作为调用者;如果被调方法是静态方法,则默认使用类作为调用者。也就是说,表面上看起来某些方法可以被独立执行,但实际上环视使用this或者 类 作为调用者。
不可以,Java的参数传递方式只有一种,值传递。所谓值传递,就是将实际参数值的副本(复制品)传入方法内,而参数本身不会受到影响。
我们有时候见到 使用 参数传递 某些对象。看起来好像是引用传递,但其实不是,这里传递的也只是一个对象在内存中的的地址而已,并不是真正的把 对象引用传递过去。
Jdk1.5 之后,Java允许定义形参个数可变的参数,从而允许为方法指定数量不确定的形参。如果在定义方法时,在最后一个形参的类型后增加三点 (…),则表明该形参可以接受多个参数值,多个参数值被当成数组传入。
public class MyClass {
private static int age;
public static void main(String[] args) {
Demo(2,3,4);
Demo(new int[]{12,2});
}
static void Demo(int... a2) {
for (int i : a2) {
System.out.println(i);
}
}
}
但需要注意的是,长度可变的形参只能处于形参列表的最后。一个方法最多只能包含一个长度可变的形参。长度可变的形参本质就是一个数组类型的形参,因此调用包含一个长度可变形参的方法时,这个长度可变的形参即可以传入多个参数,也可以传入一个数组。
对于 int f()和 void f() 两个方法,如果 int a=f(),系统可以识别是调用返回值类型为 int 的方法;但Java 调用方法时 可以忽略 方法的返回值,如果直接调用 f(),那么谁有知道到底是调用了那个方法呢?
Java类里实例变量的 set 和get方法有着非常重要意义,例如,某个类里包含了一个名为 abc 的实例变量,则其对应的 set和get 方法名应为setAbc() 和 getAbc(). 如果一个类 Java 类的每个实例 变量都被使用 private 修饰,并为每个实例变量都提供了 public 修饰的 set 和 get方法,那么这个类就是一个 符合 JavaBean 规范的类,因此,JavaBean 总是一个封装良好的类。
一个类常常就是一个小的模块,应该只让这个模块公开必须让外界知道的内容,而隐藏其他的一切内容。进行程序设计时,应尽量避免一个模块直接操作和访问另一个模块的数据,模块设计追求 高内聚(尽可能把模块之间的内部数据,功能实现细节隐藏在模块内部独立完成,不允许外部直接干预),低耦合(仅暴露少量的方法给外部使用)。
不是,构造器是创建 Java 对象的重要途径,通过 new 关键字调用构造器时,构造器也确实返回了 该类的对象,但这个对象并不是完全由构造器负责创建的。实际上,当程序员调用构造器时,系统会先为该对象分配内存空间,并为这个对象执行默认初始化,这个对象已经产生了——这些操作系统在构造器的执行前就都已经完成了。也就是说,当系统开始执行构造器的执行体之前,系统已经创建了一个对象,只是这个对象还不能被外部程序访问,只能在该构造器中通过this 来引用。当构造器的执行体结束后,这个对象作为构造器的返回值而被返回,通常还会赋给另一个引用类型的变量,从而让外部程序可以访问该对象。
如果需要在子类方法中调用 父类被覆盖的实例方法。则可使用 super 限定来调用父类被覆盖的实例方法。
package com.example.javatest;
public class Demo extends A{
public static void main(String[] args) {
new Demo().test();
}
public void test(){
super.test();
System.out.println("Demo");
}
}
class A{
public void test(){
System.out.println("A");
}
}
Java 引用变量有两个类型,一个是编译型类型,一个是运行时类型,编译时类型由声明该变量时使用的类型决定,运行时类型由实际赋给变量的对象决定。如果编译时类型和运行时类型不一样。就可能出现所谓的多态。
当把一个子了i对象那个直接赋给父类引用变量时,运行时调用该引用变量的方法是,其方法行为总是表现出子类方法的行为特征,而不是父类方法的行为特征。
判断是否是可以成功转换 instanceof 运算符的前一个操作数通常是一个 引用类型变量,后一个操组数通常是一个类(也可以是接口,可以把接口理解成一种特殊的类),它用于判断前面的对象是否是后面的类,或者其子类,实现类的实例。如果是,返回 true,否则 返回false
public class Demo {
public static void main(String[] args) {
Object a=new String("123");
if (a instanceof Integer) {
System.out.println("123");
}
}
}
继承最大的好处就是类的复用性,子类可以直接调用父类的所有成员变量和方法。但是继承带来高度复合的同事,也带来了一个严重的问题:继承严重破坏了父类的封装性。Java 里对封装的定义是:每个类都应该封装它内存信息和实现细节,而只暴露必要的方法给其他类使用。但在继承关系中,子类可以直接访问父类的成员变量(内部信息)和方法,从而造成子类和父类的严重耦合。
所以我们在使用继承的时候。首先得明白:子类是一种能特殊的父类。
对于具备以下条件之一才使用继承,否则使用 组合 也能实现类的复用。
把一个类当做一个类的组合成分,从而允许新类直接复用该类的 public 方法,这种就是组合。
一般用到组合的地方,用继承也可以实现,简单来说,继承是对已有类的一番改造,以此获得一个特殊的类。简而言之,就是将一个较为抽象的类改造成能适用于某些特定需求的类,比如在原来基础上增加别的成员变量或者方法等。反之,如果两个类之间有明确的整体,部分的关系,此时就应该采用组合关系来实现复用。
总之,继承要表达的是 (is-a)的关系,而组合表达的是 有 (has-a)的关系。(is-a代表的是继承关系,has-a代表的是对象和它成员的从属关系)
自动装箱,就是可以把一个基本类型变量直接赋给对应包装类变量,或者赋给Object变量,(Object是所有类的父类,自诶对象可以直接符给父类变量),自动拆箱则则与之相反,允许直接·把包装类对象直接赋给一个对应的基本类型变量。
Java程序测试两个变量是否相等有两种方式,一种是利用== 运算符,另一种就是利用 equals方法,当时用 判断两个变量是否相等时,如果两个变量时基本类型变量,且都是数据类型,则只要两个变量的值相等,就将返回 true。但对于引用类型变量,只要他们指向同一个对象, 判断才会返回true,== 不可用与比较类型上没有父子·关系的两个对象。euqals 判断的是引用对象里包含的字符序列是否相同,相同就返回true,
final修饰符可用于修饰类,变量和方法。final 修饰的变量度不可被改变,一旦获得了初始值,该 final 变量的值就不能被重新赋值。final修饰的成员变量必须由程序员显示的指定初始值,因为系统不会为 final 修饰的变量隐式初始化。
当时用final 修饰基本类型数据时,不能对基本类型变量重新赋值,因此基本类型变量不能被改变。但对于引用类型变量而言,它保存的仅仅是一个引用,final 只保证这个引用类型变量所引用的地址不会改变,即一直引用同一个对象,但这个对象完全可以发生改变。
当定义final 变量时就为该变量指定了初始值,而且该初始值可以在编译期间确定下来,那么这个 final 变量本质上就是一个 宏变量 ,编译器会把程序中所有用到该变量的地方直接替换为该变量的值。
需要注意的是:
抽象类不能创建实例,只能当成父类来继承。从语义的角度来说,抽象类是从多个具体类中抽象出来的父类,他具有更高层此的抽象。从多个具有相同特征的类中抽象出了一个抽象类,以这个抽象类作为其子类的模板,从而避免了子类设计的随意性。
抽象类体现的就是一种模板模式的设计,抽象类作为多个子类的通用模板,子类在抽象类的基础上进行扩展,改造,但子类总体上会大致保留抽象类的行为方式。
在java8里,允许为接口定义默认方法,类方法。
接口从宏观上来说,是从多个相似类中抽象出来的规范,接口不提供任何实现。说简单点,接口反映的是一类事物的方法。
接口里可以包含成员变量(只能是静态常量),方法(只能是抽象实例方法,类方法或默认方法),内部类(内部接口,枚举)
接口支持多继承。
接口和抽象类具有如下相同特征:
将一个类放在另一个类的内部定义,这个定义在其他类内部的类被称为内部类,包含内部类的类也被称为外部类。
内部类的作用如下:
内部类与外部类还与如下区别:
因为静态内部类是外部类的类相关的,而不是外部类的对象相关的。也就说说,静态内部类对象不是寄生在外部类的实例中,而是寄生在外部类的本身中。当静态内部类对象存在时,并不存在一个被它寄生的外部类对象,静态内部类对象只持有外部类的类引用,没有持有外部类对象访问。如果允许静态内部类的实例方法访问外部类的实例成员,但找不到被寄生的外部类对象,这将引起错误。
遍历list 的方式
List<String> list=new ArrayList<>();
list.add("123");
list.add("123");
list.add("123");
list.add("123");
list.add("123");
list.forEach(System.out::println);
实现Runnable接口
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(123);
}
}).start();
new Thread(()-> System.out.println("123")).start();
使用匿名内部类
Runnable runnable=new Runnable() {
@Override
public void run() {
System.out.println(123);
}
};
Runnable r1=()-> System.out.println("123");
r1.run();
如下写法
public class MyClass {
public static void main(String[] args) {
new MyClass().eat(()-> System.out.println("132"));
new MyClass().fly(weather -> {
System.out.println("今天");
});
new MyClass().test((a,b)->a+b);
}
public void eat(Eatable e){
System.out.println(e);
e.taste();
}
public void fly(Flyable f){
f.fly("Petterp");
}
public void test(Addable a){
a.add(5,3);
}
}
interface Eatable{
void taste();
}
interface Flyable{
void fly(String weather);
}
interface Addable{
int add(int a,int b);
}
枚举类是一种特殊的类,他一样可以有自己的成员变量,方法,可以实现一个或者多个接口,也可以定义自己的构造器。一个 Java源文件中最多只能定义一个 public 访问权限的枚举类,且该 Java 源文件也必须和枚举类的类名相同。 枚举类与普通类之间有如下区别:
public enum SeasonEnum {
a,b,c,d;
public String name;
}
class Demo{
public static void main(String[] args) {
for (SeasonEnum s:SeasonEnum.values()){
//打印枚举值的索引
System.out.println(s.ordinal());
}
//如果该枚举对象位于指定枚举对象之后,则返回正整数;
//如果该枚举对象位于指定枚举对象之前,则返回负整数,否则返回0
System.out.println(SeasonEnum.valueOf("a").compareTo(SeasonEnum.valueOf("b")));
//创建枚举类对象
SeasonEnum seasonEnum=Enum.valueOf(SeasonEnum.class,"a");
seasonEnum.name="Petterp";
System.out.println(seasonEnum.name);
}
}
枚举类也可以用于 switch
枚举类带构造器的使用
public enum SeasonEnum {
//此处的枚举值必须调用对应的构造器来创建
MALE("男"), FEmale("女");
private final String name;
//枚举类的构造器只能使用private修饰
SeasonEnum(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
class Demo {
public static void main(String[] args) {
System.out.println(SeasonEnum.FEmale);
}
}
以上代码如同于以下代码
public static final SeasonEnum MALE=new SeasonEnum("男");
public static final SeasonEnum FEmale=new SeasonEnum("女");
在枚举类中列出枚举值是,实际上就是调用构造器创建枚举类对象,只是这里无须使用new 关键字,也无需显示调用构造器。前面列出枚举值是无须传入参数,甚至无须使用括号,仅仅是因为前面的枚举类包含无参数的构造器。
枚举类也可以实现一个或多个接口,与普通类实现一个或多个接口完全一样,枚举类实现一个或多个接口时,也需要实现该接口所包含的方法。
public enum SeasonEnum implements Port {
//此处的枚举值必须调用对应的构造器来创建
MALE("男") {
@Override
public void info() {
System.out.println("这个枚举值代表男性");
}
},
FEmale("女") {
@Override
public void info() {
System.out.println("这个枚举值代表女性");
}
};
private final String name;
//枚举类的构造器只能使用private修饰
SeasonEnum(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
class Demo {
public static void main(String[] args) {
SeasonEnum.FEmale.info();
}
}
上面的代码看起来有些奇怪,当创建 MALE 和 FEMALE 两个枚举值时,后面又紧跟了一对花括号,这队花括号包含了一个 info 方法定义。花括号部分实际上就是一个类体部分,在这种情况下,当创建 MALE,FEMALE 枚举值是,并不是直接创建 SeasonEnum枚举类的实例,而是相当于创建 SeasonEnum的匿名子类的实例。因为粗体字括号部分实际上是匿名内部类的类体部分,所以这个部分的代码语法与匿名内部类语法大致相似,只是它依然是枚举类的匿名内部类。
并不是所有的枚举类都是用了 final 修饰,非抽象的枚举类才默认使用 final 修饰,对于一个抽象的枚举类而言,只要它包含了抽象方法,它就是抽象枚举类,系统会默认是使用 abstart修饰。
public enum Operation {
Plus {
@Override
public double eval(double x, double y) {
return 0;
}
},
MINUS{
public double eval(double x,double y){
return x-y;
}
};
//为枚举类定义一个抽象方法
//这个抽象方法由不同的枚举值提供不同的实现
public abstract double eval(double x,double y);
public static void main(String[] args) {
System.out.println(Operation.Plus.eval(3,4));
}
//枚举类里定义抽象方法是不能使用 abstart 关键字将枚举类定义成抽象类(因为系统自动会为它添加 abstart 关键字),但因为枚举类需要显示创建枚举值而不是作为父类,所以定义每个枚举值时必须为抽象方法提供实现,否则将出现编译错误。