final 修饰什么?
Java中,final关键字可以用来修饰类、方法和变量(包括成员变量和局部变量)。下面就从这三个方面来了解一下final关键字的基本用法。
1.修饰类
当用final修饰一个类时,表明这个类不能被继承。也就是说,如果一个类你永远不会让他被继承,就可以用final进行修饰。final类中的成员变量可以根据需要设为final,但是要注意final类中的所有成员方法都会被隐式地指定为final方法。
在使用final修饰类的时候,要注意谨慎选择,除非这个类真的在以后不会用来继承或者出于安全的考虑,尽量不要将类设计为final类。
2.修饰方法
下面这段话摘自《Java编程思想》第四版第143页:
“使用final方法的原因有两个。第一个原因是把方法锁定,以防任何继承类修改它的含义;第二个原因是效率。在早期的Java实现版本中,会将final方法转为内嵌调用。但是如果方法过于庞大,可能看不到内嵌调用带来的任何性能提升。在最近的Java版本中,不需要使用final方法进行这些优化了。“
因此,如果只有在想明确禁止 该方法在子类中被覆盖的情况下才将方法设置为final的。
注:类的private方法会隐式地被指定为final方法。
3.修饰变量
修饰变量是final用得最多的地方,首先了解一下final变量的基本语法:
对于一个final变量,如果是基本数据类型的变量,则其数值一旦在初始化之后便不能更改;如果是引用类型的变量,则在对其初始化之后便不能再让其指向另一个对象。
一句话:final可以修饰:类 变量 方法;final修饰的类不能被继承;final修饰的方法不能被重写;final修饰的变量是一个常量,只能背赋值一次。
static关键字能修饰什么?
static详解:https://www.cnblogs.com/dolphin0520/p/10651845.html
static关键字是Java中常用的关键字之一,可能最常用的就是修饰变量和方法了,但是仅仅知道这些还远远不够。问题虽然很小,但是却反映了对Java的了解程度。
static修饰变量和方法
static可以修饰变量,这个变量属于类本身,不需要创建实例就可以直接获取到值。
static可以修饰方法,这个方法属于类本身,同样,不要创建实例就可以通过类调用。
需要了解的是,static修饰的变量或方法属于类的静态资源,是所有实例共享的,另外静态方法内部是不能访问非静态方法的,因为静态资源是在类加载的时候就建立好的,而类加载时非静态方法需要类new的时候才能创建,一前一后的顺序所以导致了Java静态方法不能访问非静态资源的结果,当然反之肯定是可以的了。
static修饰代码块
static修饰的代码块是静态代码块,也具有静态的特点,属于类本身,在加载时只需要加载一次,也就是说,如果加载过这个代码块,就不会再加载了。
static修饰类
static修饰类只有一种情况,那就是这个类属于静态内部类,接触过Android开发的话可能遇见过很多这样的静态内部类,如WindowManager.LayoutParams类,LayoutParams就是WindowManager类下的静态内部类,它的源码如下所示:
public interface WindowManager extends ViewManager {
public static class LayoutParams extends ViewGroup.LayoutParams implements Parcelable {
//内部实现
}
}
普通类是不允许声明为静态的,只有内部类才可以。
被static修饰的内部类可以直接作为一个普通类来使用,而不需先实例一个外部类。
public class OuterClass {
public static class InnerClass {
InnerClass(){
System.out.println("===== 我是一个内部类'InnerClass' =====");
}
}
}
public class TestStaticClass {
public static void main(String[] args) {
// 不需要先new一个OutClass的对象
OuterClass.InnerClass inner = new OuterClass.InnerClass();
}
}
如果没有用static修饰InterClass,则只能按如下方式调用:
public class OuterClass {
public class InnerClass{
InnerClass(){
System.out.println("===== 我是一个内部类'InnerClass' =====");
}
}
}
public class TestStaticClass {
public static void main(String[] args) {
// 需要先new一个OuterClass的对象outer
// 然后通过outer.new生成内部类的对象
OuterClass outer = new OuterClass();
OuterClass.InnerClass inner = outer.new InnerClass();
}
}
import static
这个算是比较冷门的,但是知识不怕多,import static是JDK1.5之后的新特性,这两个关键字连用可以指定导入某个类中的指定静态资源。
import static java.lang.Math.*;
public class A
{
public static void main(String[] args)
{
System.out.println(sin(2.2));
}
}
不需要再加Math.sin()的Math了,还是很方便的,但是可读性不高,不建议这样写。
修饰内部类和外部类区别
Java内部类详解:https://www.cnblogs.com/dolphin0520/p/3811445.html
java 内部类和静态内部类的区别:https://www.cnblogs.com/aademeng/articles/6192954.html
内部类和外部类的区别和联系:https://www.cnblogs.com/NYfor2018/p/9466476.html
String s = ‘abc’ 是在哪块内存中存储
答:方法区的常量池
String s=“abc” 和 String s1=new String(“abc”)内存分析
String s=”abc”
s是栈内变量,它的内存保存的是常量池的常量字符串对象对象”abc”的地址。常量池中的每个值只有一份。String s2 = “abc”;
s和s2两个变量指向的是同一个地址,因此s==s2结果为true
String s1=new String(“abc”);
s1是栈内变量,它的内存保存的是堆中的new String对象的地址,new String在堆中生成对象,并用常量池的字符串对象”abc”初始化堆中的对象,所以堆中的对象的内存的内容和常量池对象的内存的内容一样,但是是不同的两个对象
因此s==s1的结果是false。==比较的是两对象的引用,因此结果为false.
s.equals(s1)的结果是true,因为String的equals方法比较的是对象的内容。
String s = “abc”到底做了啥
作为开发人员在类里面定义String变量几乎是家常便饭,亦或是日常了,照理说String是个对象,为什么我们定义的时候不是直接 new String(“abc”);
而是直接写了对象内容,难道不觉得有点诡异吗?
String s1 = “abc”;
String s2 = “abc”;
String s3 = new String(“abc”);
System.out.println("s1 = s2? " + (s1 == s2)); //true
System.out.println("s1 = s3? " + (s1 == s3)); //false
怎么去理解上面的结果,大家知道字面量赋值是在常量池,常量池其实是一张表:
常量池(Constant Pool)指的是在编译期被确定,并被保存在已编译的.class文件中的一些数据。JVM虚拟机为每个被装载的类型维护一个常量池。常量池就是该类型所用到常量的一个有序集和,包括直接常量(String,Integer和 Floating point常量)和对其他类型,字段和方法的符号引用。对于String常量,它的值是在常量池中的。而JVM中的常量池在内存当中是以表的形式存在的, 对于String类型,有一张固定长度的CONSTANT_String_info表用来存储文字字符串值,注意:该表只存储文字字符串值,不存储符号引用。
1.Strings = “abc”;
创建过程分析:在class文件被JVM装载到内存中,JVM会创建一块String Pool(String缓冲池)。当执行String s = “abc”;时,JVM首先在String Pool中查看是否存在字符串对象“abc”(如何查看呢?用equals()方法判断),如果已存在该对象,则不用创建新的字符串对象“abc”,而直接使用String Pool中已存在的对象“abc”,然后将引用s指向该对象;如果不存在该对象,则先在String Pool中创建一个新的字符串对象“abc”,然后将引用s指向String Pool中创建的新对象。
注意:使用“字符串常量”引号创建的字符串对象时,在编译期就已经确定将该对象存储到String Pool中了。因此,String s = “abc”只会在编译期,在String Pool中创建一个对象。
2.new String(“abc”);
将会在堆中创建对象,查看下String构造方法源码
private final char value[];
public String(String original) {
this.value = original.value;
this.hash = original.hash;
}
很明显在堆中String其实是char数组维护一组数据,前者是表后者是数组,结构不一样,地址存放不一样,引用自然不一样,虽然内容是一样!