Java 是很多人一直在用的编程语言,但是有些 Java 概念是非常难以理解的,哪怕是一些多年的老手,对某些 Java 概念也存在一些混淆和困惑。
所以,在这篇文章里,会介绍四个 Java 中最难理解的四个概念,去帮助开发者更清晰的理解这些概念:
匿名内部类又叫匿名类,它有点像局部类(Local Class)或者内部类(Inner Class),只是匿名内部类没有名字,我们可以同时声明并实例化一个匿名内部类。
一个匿名内部类仅适用在想使用一个局部类并且只会使用这个局部类一次的场景。
匿名内部类是没有需要明确声明的构造函数的,但是会有一个隐藏的自动声明的构造函数。
咱们看看下面的例子:
// 接口:程序员 interface Programmer { void develop(); } public class TestAnonymousClass { public static Programmer programmer = new Programmer() { @Override public void develop() { System.out.println("我是在类中实现了接口的匿名内部类"); } }; public static void main(String[] args) { Programmer anotherProgrammer = new Programmer() { @Override public void develop() { System.out.println("我是在方法中实现了接口的匿名内部类"); } }; TestAnonymousClass.programmer.develop(); anotherProgrammer.develop(); } }
从上面的例子可以看出,匿名类既可以在类中也可以在方法中被创建。
之前我们也提及匿名类既可以继承一个具体类或者抽象类,也可以实现一个接口。所以在上面的代码里,我创建了一个叫做 Programmer 的接口,并在 TestAnonymousClass 这个类中和 main() 方法中分别实现了接口。
Programmer除了接口以外既可以是一个抽象类也可以是一个具体类。
抽象类,像下面的代码一样:
public abstract class Programmer { public abstract void develop(); }
具体类代码如下:
public class Programmer { public void develop() { System.out.println("我是一个具体类"); } }
OK,继续深入,那么如果 Programmer 这个类没有无参构造函数怎么办?我们可以在匿名类中访问类变量吗?我们如果继承一个类,需要在匿名类中实现所有方法吗?
public class Programmer { protected int age; public Programmer(int age) { this.age = age; } public void showAge() { System.out.println("年龄:" + age); } public void develop() { System.out.println("开发中……除了异性,他人勿扰"); } public static void main(String[] args) { Programmer programmer = new Programmer(38) { @Override public void showAge() { System.out.println("在匿名类中的showAge方法:" + age); } }; programmer.showAge(); } }
临时使用:我们有时候需要添加一些类的临时实现去修复一些问题或者添加一些功能。为了避免在项目里添加java文件,尤其是仅使用一次这个类的时候,我们就会使用匿名类。UI Event Listeners:在java的图形界面编程中,匿名类最常使用的场景就是去创建一个事件监听器。比如:
button.setOnClickListener(new View.OnClickListener() { public void onClick(View v) { } });
上面的代码中,我们通过匿名类实现了 setOnClickListener 接口,当用户点击按钮的时候,就会触发我们实现的 onClick 方法。
Java 中的多线程就是利用多个线程共同完成一个大任务的运行过程,使用多线程可以最大程度的利用CPU。
使用多线程的使用线程而不是进程来做任务处理,是因为线程比进程更加轻量,线程是一个轻量级的进程,是程序执行的最小单元,并且线程和线程之间是共享主内存的,而进程不是。
正如上图所示,线程生命周期一共有六种状态。我们现在依次对这些状态进行介绍。
大白话讲就是通过多线程同时做多件事情让 Java 应用程序跑的更快,使用线程来实行并行和并发。如今的 CPU 都是多核并且频率很高,如果单独一个线程,并没有充分利用多核 CPU 的优势。
重要的优势
1.通过继承Thread类创建线程
这个继承类会重写 Thread 类的 run() 方法。一个线程的真正运行是从 run() 方法内部开始的,通过 start() 方法会去调用这个线程的 run() 方法。
public class MultithreadDemo extends Thread { @Override public void run() { try { System.out.println("线程 " + Thread.currentThread().getName() + " 现在正在运行"); } catch (Exception e) { e.printStackTrace(); } } public static void main(String[] args) { for (int i = 0; i < 10; i++) { MultithreadDemo multithreadDemo = new MultithreadDemo(); multithreadDemo.start(); } } }
2.通过实现Runnable接口创建线程
我们创建一个实现了 java.lang.Runnable 接口的新类,并实现其 run() 方法。然后我们会实例化一个 Thread 对象,并调用这个对象的 start() 方法。
public class MultithreadDemo implements Runnable { @Override public void run() { try { System.out.println("线程 " + Thread.currentThread().getName() + " 现在正在运行"); } catch (Exception e) { e.printStackTrace(); } } public static void main(String[] args) { for (int i = 0; i < 10; i++) { Thread thread = new Thread(new MultithreadDemo()); thread.start(); } } }
同步只有在多线程条件下才有意义,一次只能有一个线程执行同步块。
在 Java 中,同步这个概念非常重要,因为 Java 本身就是一门多线程语言,在多线程环境中,做合适的同步是极度重要的。
在多线程环境中执行代码,如果一个对象可以被多个线程访问,为了避免对象状态或者程序执行出现错误,对这个对象使用同步是非常必要的。
在深入讲解同步概念之前,我们先来看看同步相关的问题。
class Production { //没有做方法同步 void printProduction(int n) { for (int i = 1; i <= 5; i++) { System.out.print(n * i+" "); try { Thread.sleep(400); } catch (Exception e) { System.out.println(e); } } } } class MyThread1 extends Thread { Production p; MyThread1(Production p) { this.p = p; } public void run() { p.printProduction(5); } } class MyThread2 extends Thread { Production p; MyThread2(Production p) { this.p = p; } public void run() { p.printProduction(100); } } public class SynchronizationTest { public static void main(String args[]) { Production obj = new Production(); //多线程共享同一个对象 MyThread1 t1 = new MyThread1(obj); MyThread2 t2 = new MyThread2(obj); t1.start(); t2.start(); } }
运行上面的代码后,由于我们没有加同步,可以看到运行结果非常混乱。
Output:
100 5 10 200 15 300 20 400 25 500
接下来,我们给 printProduction 方法加上同步:
class Production { //做了方法同步 synchronized void printProduction(int n) { for (int i = 1; i <= 5; i++) { System.out.print(n * i+" "); try { Thread.sleep(400); } catch (Exception e) { System.out.println(e); } } } }
当我们对 printProduction() 加上了同步(synchronized)后, 已有一个线程执行的情况下,是不会有任何一个线程可以再次执行这个方法。这次加了同步后的输出结果是有次序的。
Output:
5 10 15 20 25 100 200 300 400 500
类似于对方法做同步,你也可以去同步 Java 类和对象。
注意:其实有时候我们可以不必去同步整个方法。出于性能原因,我们其实可以仅同步方法中我们需要同步的部分代码。被同步的这部分代码就是方法中的同步块。
Java 的序列化就是将一个 Java 对象转化为一个字节流的一种机制。从字节流再转回 Java 对象叫做反序列化,是序列化的反向操作。
序列化和反序列化是和平台无关的,也就是说你可以在 Linux 系统序列化,然后在 Windows 操作系统做反序列化。
如果要序列化对象,需要使用 ObjectOutputStream 类的 writeObject() 方法。如果要做反序列化,则要使用 ObjectOutputStream 类的 readObject() 方法。
如下图所示,对象被转化为字节流后,被储存在了不同的介质中。这个流程就是序列化。在图的右边,也可以看到从不同的介质中,比如内存,获得字节流并转化为对象,这叫做反序列化。
如果我们创建了一个 Java 对象,这个对象的状态在程序执行完毕或者退出后就消失了,不会得到保存。
所以,为了能解决这类问题,Java 提供了序列化机制。这样,我们就能把对象的状态做临时储存或者进行持久化,以供后续当我们需要这个对象时,可以通过反序列化把对象还原回来。
下面给出一些代码看看我们是怎么来做序列化的。
import java.io.Serializable; public class Player implements Serializable { private static final long serialVersionUID = 1L; private String serializeValueName; private transient String nonSerializeValuePos; public String getSerializeValueName() { return serializeValueName; } public void setSerializeValueName(String serializeValueName) { this.serializeValueName = serializeValueName; } public String getNonSerializeValueSalary() { return nonSerializeValuePos; } public void setNonSerializeValuePos(String nonSerializeValuePos) { this.nonSerializeValuePos = nonSerializeValuePos; } @Override public String toString() { return "Player [serializeValueName=" + serializeValueName + "]"; } }
import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectOutputStream; public class SerializingObject { public static void main(String[] args) { Player playerOutput = null; FileOutputStream fos = null; ObjectOutputStream oos = null; playerOutput = new Player(); playerOutput.setSerializeValueName("niubi"); playerOutput.setNonSerializeValuePos("x:1000,y:1000"); try { fos = new FileOutputStream("Player.ser"); oos = new ObjectOutputStream(fos); oos.writeObject(playerOutput); System.out.println("序列化数据被存放至Player.ser文件"); oos.close(); fos.close(); } catch (IOException e) { e.printStackTrace(); } } }
Output:
序列化数据被存放至Player.ser文件
import java.io.FileInputStream; import java.io.IOException; import java.io.ObjectInputStream; public class DeSerializingObject { public static void main(String[] args) { Player playerInput = null; FileInputStream fis = null; ObjectInputStream ois = null; try { fis = new FileInputStream("Player.ser"); ois = new ObjectInputStream(fis); playerInput = (Player) ois.readObject(); System.out.println("从Player.ser文件中恢复"); ois.close(); fis.close(); } catch (IOException | ClassNotFoundException e) { e.printStackTrace(); } System.out.println("player名字为 : " + playerInput.getSerializeValueName()); System.out.println("player位置为 : " + playerInput.getNonSerializeValuePos()); } }
Output:
从Player.ser文件中恢复
player名字为 : niubi
player位置为:null
首先,我们介绍了匿名类的定义,使用场景和使用方式。
其次,我们讨论了多线程和其生命周期以及多线程的使用场景。
再次,我们了解了同步,知道同步后,仅同时允许一个线程执行被同步的方法或者代码块。当一个线程在执行被同步的代码时,别的线程只能在队列中等待直到执行同步代码的线程释放资源。
最后,我们知道了序列化就是把对象状态储存起来以供后续使用。
以上就是Java中难理解的四个概念的详细内容,更多关于Java的资料请关注脚本之家其它相关文章!
免责声明:本站发布的内容(图片、视频和文字)以原创、来自互联网转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系QQ:712375056 进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。
Copyright © 2009-2021 56dr.com. All Rights Reserved. 特网科技 特网云 版权所有 珠海市特网科技有限公司 粤ICP备16109289号
域名注册服务机构:阿里云计算有限公司(万网) 域名服务机构:烟台帝思普网络科技有限公司(DNSPod) CDN服务:阿里云计算有限公司 中国互联网举报中心 增值电信业务经营许可证B2
建议您使用Chrome、Firefox、Edge、IE10及以上版本和360等主流浏览器浏览本网站