Waste-Free Coding: Zero-Cost Abstraction in Java

Waste-Free Coding: Zero-Cost Abstraction in Java

Waste-Free Coding: Zero-Cost Abstraction in Java - Solve an event processing problem by reducing waste in the software stack...

Waste-Free Coding: Zero-Cost Abstraction in Java - Solve an event processing problem by reducing waste in the software stack...

Java is often seen as a memory hog that cannot operate efficiently in low memory environments. My aim is to demonstrate what many think is impossible, that a meaningful java program can operate in almost no memory. The example processes 2.2 million CSV records per second in a 3MB heap with zero GC on a single thread in Java.

You will learn where the main areas of waste exist in a Java application and the patterns that can be employed to reduce them. The concept of zero cost abstraction is introduced, and that many optimizations can be automated at compile time through code generation. A maven plugin simplifies the developer workflow.

Our goal is not high performance, that comes as a by-product of maximizing efficiency. The solution employs Fluxtion which uses a fraction of the resources compared with existing java event processing frameworks.

Computing and the Climate

Climate change and its causes are currently of great concern to many. Computing is a major source of emissions, producing the same carbon footprint as the entire airline industry. In the absence of regulation dictating computing energy consumption we, as engineers, have to assume the responsibility for producing efficient systems balanced against the cost to create them.

On a panel session from InfoQ 2019 in London, Martin Thompson spoke passionately about building energy-efficient computing systems. He noted controlling waste is the critical factor in minimizing energy consumption. Martin's comments resonated with me, as the core philosophy behind Fluxtion is to remove unnecessary resource consumption. That panel session was the inspiration for this article.

Processing Requirements

Requirements for the processing example are:

  • Operate in 3MB of the heap with zero GC
  • Use standard Java libraries only, no "unsafe" optimizations
  • Read a CSV file containing millions of rows of input data
  • Input is a set of unknown events, no pre-loading of data
  • Data rows are heterogeneous types
  • Process each row to calculate multiple aggregate values
  • Calculations are conditional on the row type and data content
  • Apply rules to aggregates and count rule breaches
  • Data is randomly distributed to prevent branch prediction
  • Partition calculations based on row input values
  • Collect and group partitioned calculations into an aggregate view
  • Publish a summary report at the end of file
  • Pure Java solution using high-level functions
  • No JIT warm-up
Example Position and Profit Monitoring

The CSV file contains trades and prices for a range of assets, one record per row. Position and profit calculations for each asset are partitioned in their own memory space. Asset calculations are updated on every matching input event. Profits for all assets will be aggregated into a portfolio profit. Each asset monitors its current position/profit state and records a count if either breaches a pre-set limit. The profit of the portfolio will be monitored and loss breaches counted.

Rules are validated at the asset and portfolio level for each incoming event. Counts of rule breaches are updated as events are streamed into the system.

Row Data Types

AssetPrice - [price: double] [symbol: CharSequence]
Deal       - [price: double] [symbol: CharSequence] [size: int]

Sample Data

The CSV file has a header lines for each type to allow dynamic column position to field mapping. Each row is preceded with the simple class name of the target type to marshal into. A sample set of records including header:

Deal,symbol,size,price
AssetPrice,symbol,price
AssetPrice,FORD,15.0284
AssetPrice,APPL,16.4255
Deal,AMZN,-2000,15.9354

Calculation Description

Asset calculations are partitioned by a symbol and then gathered into a portfolio calculation.

Partitioned Asset Calculations

asset position  = sum(Deal::size)
deal cash value = (Deal::price) X (Deal::size) X -1
cash position   = sum(deal cash value)
mark to market  = (asset position) X (AssetPrice::price)
profit          = (asset mark to market) + (cash position)

Portfolio Calculations

portfolio profit = sum(asset profit)

Monitoring Rules

asset loss > 2,000
asset position outside of range +- 200
portfolio loss > 10,000

NOTE:

  1. A count is made when a notifier indicates a rule breach. The notifier only fires on the first breach until it is reset. The notifier is reset when the rule becomes valid again.
  2. A positive deal::size is a buy, a negative value a sell.
Execution Environment

To ensure memory requirements are met (zero GC and 3MB heap), the Epsilon no-op garbage collector is used, with a max heap size of 3MB. If more than 3MB of memory is allocated throughout the life of the process, the JVM will immediately exit with an out of memory error.

To run the sample: clone from git, and in the root of the trading-monitor project, run the jar file in the dist directory to generate a test data file of 4 million rows.

git clone --branch  article_may2019 https://github.com/gregv12/articles.git
cd articles/2019/may/trading-monitor/
jdk-12.0.1\bin\java.exe -jar dist\tradingmonitor.jar 4000000

Input Data

By default. the tradingmonitor.jar processes the data/generated-data.csv file. Using the command above, the input data should have the following characteristics:

Input file    = data/generated-data.csv
Trade count   =   200,000
price updates = 3,800,000
Asset count   =         7
File size     =        96 MB

A utility is provided to generate input data files of different data sizes, see GenerateTestData.java.

Results

To execute the test, run the tradingmonitor.jar with no arguments:

jdk-12.0.1\bin\java.exe -verbose:gc -Xmx3M -XX:+UnlockExperimentalVMOptions -XX:+UseEpsilonGC -jar dist\tradingmonitor.jar

Executing the test for 4 million rows the summary results are:

Process row count     =    4 million
Processing time       =    1.815 seconds
Avg row exec time     =  453 nano seconds
Process rate          =    2.205 million records per second
garbage collections   =    0
allocated mem total   = 2857 KB
allocated mem per run =   90 KB
OS                    = windows 10
Processor             = Intel core [email protected]
Memory                = 16 GB
Disk                  = 512GB Samsung SSD PM961 NVMe

NOTE: Results are from the first run without JIT warm-up. After JIT warm-up, the code execution times are approx 10 percent quicker. Total allocated memory is 2.86MB, which includes starting the JVM.

Analysing Epsilon's output, we estimate the app allocates 15 percent of memory for 6 runs, or 90KB per run. There is a good chance the application data will fit inside L1 cache, but more investigations are required here.

Output

The test program loops 6 times. Printing out the results each time, Epsilon records memory statistics at the end of the run.

jdk-11.0.1\bin\java.exe" -server -XX:+UnlockExperimentalVMOptions -XX:+UseEpsilonGC  -Xmx3M -verbose:gc -jar dist\tradingmonitor.jar
[0.014s][info][gc] Non-resizeable heap; start/max: 3M
[0.015s][info][gc] Using TLAB allocation; max: 4096K
[0.016s][info][gc] Elastic TLABs enabled; elasticity: 1.10x
[0.018s][info][gc] Elastic TLABs decay enabled; decay time: 1000ms
[0.019s][info][gc] Using Epsilon
[0.057s][info][gc] Heap: 3M reserved, 3M (100.00%) committed, 0M (5.45%) used
.....
[0.263s][info][gc] Heap: 3M reserved, 3M (100.00%) committed, 2M (73.88%) used
portfolio loss gt 10k count -> 1894405.0
Portfolio PnL:-1919.9455999995407
Assett positions:
-----------------------------
MSFT : AssetTradePos{symbol=MSFT, pnl=-388.0564999999906, assetPos=420.0, mtm=7323.246, cashPos=-7711.302499999991, positionBreaches=156, pnlBreaches=51015}
GOOG : AssetTradePos{symbol=GOOG, pnl=-359.2007000000076, assetPos=319.0, mtm=5135.4215, cashPos=-5494.622200000008, positionBreaches=335, pnlBreaches=104282}
APPL : AssetTradePos{symbol=APPL, pnl=14.577800000011848, assetPos=-600.0, mtm=-9936.42, cashPos=9950.997800000012, positionBreaches=46, pnlBreaches=69790}
ORCL : AssetTradePos{symbol=ORCL, pnl=72.58320000000003, assetPos=-404.0, mtm=-6327.0036, cashPos=6399.5868, positionBreaches=66, pnlBreaches=40570}
FORD : AssetTradePos{symbol=FORD, pnl=-1339.9584999997278, assetPos=1359.0, mtm=21383.457300000002, cashPos=-22723.41579999973, positionBreaches=5, pnlBreaches=116290}
BTMN : AssetTradePos{symbol=BTMN, pnl=579.833700000183, assetPos=-513.0, mtm=-8037.8379, cashPos=8617.671600000183, positionBreaches=29, pnlBreaches=106779}
AMZN : AssetTradePos{symbol=AMZN, pnl=-499.72460000000956, assetPos=79.0, mtm=1249.1322, cashPos=-1748.8568000000096, positionBreaches=68, pnlBreaches=27527}
millis:1814
...
...
starting
portfolio loss gt 10k count -> 1894405.0
Portfolio PnL:-1919.9455999995407
Assett positions:
-----------------------------
MSFT : AssetTradePos{symbol=MSFT, pnl=-388.0564999999906, assetPos=420.0, mtm=7323.246, cashPos=-7711.302499999991, positionBreaches=156, pnlBreaches=51015}
GOOG : AssetTradePos{symbol=GOOG, pnl=-359.2007000000076, assetPos=319.0, mtm=5135.4215, cashPos=-5494.622200000008, positionBreaches=335, pnlBreaches=104282}
APPL : AssetTradePos{symbol=APPL, pnl=14.577800000011848, assetPos=-600.0, mtm=-9936.42, cashPos=9950.997800000012, positionBreaches=46, pnlBreaches=69790}
ORCL : AssetTradePos{symbol=ORCL, pnl=72.58320000000003, assetPos=-404.0, mtm=-6327.0036, cashPos=6399.5868, positionBreaches=66, pnlBreaches=40570}
FORD : AssetTradePos{symbol=FORD, pnl=-1339.9584999997278, assetPos=1359.0, mtm=21383.457300000002, cashPos=-22723.41579999973, positionBreaches=5, pnlBreaches=116290}
BTMN : AssetTradePos{symbol=BTMN, pnl=579.833700000183, assetPos=-513.0, mtm=-8037.8379, cashPos=8617.671600000183, positionBreaches=29, pnlBreaches=106779}
AMZN : AssetTradePos{symbol=AMZN, pnl=-499.72460000000956, assetPos=79.0, mtm=1249.1322, cashPos=-1748.8568000000096, positionBreaches=68, pnlBreaches=27527}
millis:1513
[14.870s][info][gc] Total allocated: 2830 KB
[14.871s][info][gc] Average allocation rate: 19030 KB/sec

Waste Hotspots

The table below identifies functions in the processing loop that traditionally create waste and waste avoidance techniques utilized in the example.

Waste Reduction Solutions

The code that implements the event processing is generated using Fluxtion. Generating a solution allows for a zero-cost abstraction approach where the compiled solution has minimum overhead. The programmer describes the desired behavior, and at build time, an optimized solution is generated that meets the requirements. For this example, the generated code can be viewed here.

The Maven POM contains a profile for rebuilding the generated files using the Fluxtion Maven plugin executed with the following command:

mvn -Pfluxtion install

File Reading

Data is extracted from the input file as a series of CharEvents and published to the CSV-type marshaller. Each character is individually read from the file and pushed into a CharEvent. As the same CharEvent instance is re-used, no memory is allocated after initialization. The logic for streaming CharEvents is located in the CharStreamer class. The whole 96 MB file can be read with almost zero memory allocated on the heap by the application.

CSV Processing

Adding a @CsvMarshaller to a javabean notifies Fluxtion to generate a CSV parser at build time. Fluxtion scans application classes for the @CsvMarshaller annotation and generates marshallers as part of the build process. For example, see AssetPrice.java, which results in the generation of AssetPriceCsvDecoder0. The decoder processes CharEvents and marshalls the row data into a target instance.

The generated CSV parsers employ the strategies outlined in the table above, avoiding any unnecessary memory allocation and re-using object instances for each row processed:

  • A single re-usable instance of a characters buffer stores the row characters
  • A flyweight re-usable instance is the target for the marshaled row data
  • Conversions are performed directly from a CharSequence into target types without intermediate object creation.
  • If CharSequences are used in the target instance field, then no strings are created; a flyweight Charsequence is used.

For example, a waste-free char targets field conversion and sees the upateTarget() method in a AssetPriceCsvDecoder0; this is generated by Fluxtion:

private boolean updateTarget() {
  try {
    updateFieldIndex();
    fieldIndex = fieldName_price;
    setPrice.subSequence(delimIndex[fieldName_price], delimIndex[fieldName_price + 1] - 1);
    target.setPrice(atod(setPrice));
    fieldIndex = fieldName_symbol;
    setSymbol.subSequence(delimIndex[fieldName_symbol], delimIndex[fieldName_symbol + 1] - 1);
    target.setSymbol(setSymbol);
  } catch (Exception e) {
....
  }
  return true;
}

Calculations

This builder describes the asset calculation using the Fluxtion streaming API. The declarative form is similar to the Java stream API but builds real-time event processing graphs. Methods marked with the annotation @SepBuilder are invoked by the Maven plugin to generate a static event processor. The code below describes the calculations for an asset, see FluxtionBuilder:

@SepBuilder(name = "SymbolTradeMonitor",
            packageName = "com.fluxtion.examples.tradingmonitor.generated.symbol",
            outputDir = "src/main/java",
            cleanOutputDir = true
    )
    public void buildAssetAnalyser(SEPConfig cfg) {
        //entry points subsrcibe to events
        Wrapper<Deal> deals = select(Deal.class);
        Wrapper<AssetPrice> prices = select(AssetPrice.class);
        //result collector, and republish as an event source
        AssetTradePos results = cfg.addPublicNode(new AssetTradePos(), "assetTradePos");
        eventSource(results);
        //calculate derived values
        Wrapper<Number> cashPosition = deals
                .map(multiply(), Deal::getSize, Deal::getPrice)
                .map(multiply(), -1)
                .map(cumSum());
        Wrapper<Number> pos = deals.map(cumSum(), Deal::getSize);
        Wrapper<Number> mtm = pos.map(multiply(), arg(prices, AssetPrice::getPrice));
        Wrapper<Number> pnl = add(mtm, cashPosition);
        //collect into results
        cashPosition.push(results::setCashPos);
        pos.push(results::setAssetPos);
        mtm.push(results::setMtm);
        pnl.push(results::setPnl);
        deals.map(count()).push(results::setDealsProcessed);
        prices.map(count()).push(results::setPricesProcessed);
        //add some rules - only fires on first breach
        pnl.filter(lt(-200))
                .notifyOnChange(true)
                .map(count())
                .push(results::setPnlBreaches);
        pos.filter(outsideBand(-200, 200))
                .notifyOnChange(true)
                .map(count())
                .push(results::setPositionBreaches);
        //human readable names to nodes in generated code - not required 
        deals.id("deals");
        prices.id("prices");
        cashPosition.id("cashPos");
        pos.id("assetPos");
        mtm.id("mtm");
        pnl.id("pnl");
    }

The functional description is converted into an efficient imperative form for execution. A generated event processor, SymbolTradeMonitor, is the entry point for AssetPrice and Deal events. Generated helper classes are used by the event processor to calculate the aggregates; the helper classes are here.

The processor receives events from the partitioner and invokes helper functions to extract data and call calculation functions, storing aggregate results in nodes. Aggregate values are pushed into fields of the results instance, AssetTradePos. No intermediate objects are created; any primitive calculation is handled without auto-boxing. Calculation nodes reference data from parent instances, no data objects are moved around the graph during execution. Once the graph is initialized, there are no memory allocations when an event is processed.

An image representing the processing graph for an asset calculation is generated at the same time as the code, which can be seen below:

A similar set of calculations is described for the portfolio in the FluxtionBuilderbuilder class buildPortfolioAnalyser method, generating a PortfolioTradeMonitor event.

The AssetTradePos is published from a SymbolTradeMonitor to the PortfolioTradeMonitor.

The generated files for the portfolio calculations are located here.

Partitioning and Gathering

All calculations, partitioning and gathering operations happen in the same single thread; no locks are required. Immutable objects are not required as there are no concurrency issues to handle. The marshaled events have an isolated private scope, allowing safe re-use of instances as the generated event processors control the lifecycle of the instances during event processing.

System Data Flow

The diagram below shows the complete data flow for the system from bytes on a disk to the published summary report. The purple boxes are generated as part of the build; blue boxes are re-usable classes.

Conclusion

In this article, I have shown how it is possible to solve a complex event handling problem in Java with almost no waste. High-level functions were utilized in a declarative/functional approach to describing desired behavior and the generated event processors met the requirements of the description. A simple annotation triggered the marshaller generation. The generated code is a simple imperative code that the JIT can optimize easily. No unnecessary memory allocations are made, and instances are re-used as much as possible.

Following this approach, high-performance solutions with low resource consumption are within the grasp of the average programmer. Traditionally, only specialist engineers with many years of experience could achieve these results.

Although novel in Java, this approach is familiar in other languages, commonly known as zero-cost abstraction.

With today's cloud-based computing environments, resources are charged per unit consumed. Any solution that saves energy will also have a positive benefit on the company.

Java Fundamentals: Learn Java for absolute beginners |Simpliv

Java Fundamentals: Learn Java for absolute beginners |Simpliv

Java Fundamentals: Learn Java for absolute beginners

Description
This is the best course to learn to program in Java in Spanish from scratch and without any experience in this fabulous programming language. This is the first course where we will study the Fundamentals of Java, and we will take you step by step until you acquire the bases of the Java language and you can start to study more advanced Java topics.

The content is divided into perfectly structured levels, each level supported by the previous one, with the aim of adding Java knowledge incrementally and so you can focus on mastering the issues little by little and gradually. So ensure the success of your Java training.

We will also offer support for any doubts about the didactic material included in this Java Fundamentals course.

We manage a new teaching methodology that we have called Speed ​​Learning. This methodology consists of concise videos that go directly to the point to be studied, complemented by eBooks with explanations and step-by-step images (which you can print, or search for any text you need, or use for your offline study), since As we know we can not do text search within a video. In addition, our methodology includes perfectly structured and very didactic exercises that will allow you to accelerate your eLearning learning. No loss of time in videos where you have to watch the instructor codify an exercise, too much theory, little practice or anything like that. Our Speed ​​Learning methodology guarantees that in the shortest possible time you will acquire the necessary knowledge for the Java professional and working world.

The Java Fundamentals course includes the following topics for study:

Lesson 1 - Starting with Java Technology

The amazing world of Java programming

What is Java technology (from a practical approach)

Our first Java program from scratch

Lesson 2 - Variables and Operators in Java

Use of Variables in Java and what we use them for

Types of Data in Java and how they are classified

Management and Classification of operators in Java

Lesson 3 - Control statements in Java

Using the if-else structure and where to use it

Handling the switch structure and when applying it

Lesson 4 - Handling Loops in Java

Use of the for loop and its use

Using the while loop and how to apply it

Use of the do-while loop and when to use it

Lesson 5 - Object Oriented Programming

Introduction to Object Oriented Programming (OOP)

Handling Classes in Java

Use of Objects in Java

Lesson 6 - Functions in Java

Declaration of Methods or Functions in Java

Use and call of functions in Java

Lesson 7 - Data Management in Java

Using Arrays in Java

Management of Matrices in Java

Lesson 8 - Inheritance in Java

Inheritance Management in Java

Use of superclasses and subclasses in Java

Final Level Laboratory

Final Exercise where everything learned in this Level is integrated

At the end you get a certificate of having completed the Java Fundamentals course.

We wait for you on the other side.

Ing. Ubaldo Acosta

Founder of Global Mentoring

Passion for Java Technology

Who this course is for:

Anyone who wants to learn how to program in Java
Basic knowledge
Basic knowledge of PC use
Basic management of an operating system such as Windows, Mac or Linux
It is not necessary to know how to program, we will start from scratch !!!
The attitude and desire to start coding and learning Java once and for all from scratch!
What will you learn
Have the basics of the programming language with Java
You will know the basic syntax of the Java language
Manage the concept of Variables and Operators in Java
We will study Object Oriented Programming with Java
You will learn the Control Statements and Loops in Java
We will see the concept of Functions with Java
We will study the concept of Inheritance in Java
We will learn to use Arrays in java
We will handle the concept of Matrices in Java
We will learn to Design Classes in Java
We will make a final application with everything learned in the course
To know more:

Java Essentials : Learn Core Java From Basic to Advance

Java Essentials : Learn Core Java From Basic to Advance

Learn Java Programming Using Practical Assignments. Start Building Back-end Web Applications Robust Test Automation Frameworks By End Of The Course. Learn More!

Description
This is only Java related course and it's great because it covers just the right amount of Java which is needed to leaning programming, java.

This is a comprehensive yet simple course on java programming language and it concentrates on Java programming concepts.

*************************** No Prior Coding Experience Needed ***************************

This course assumes that you have no programming background. If you have some experience then, it's just a bonus point. You have never code, have some experience or have a lot of experience any other programming language, this course is one stop place for you.

Java is one of the most and useful programming languages to learn You can build back-end of web applications and build robust test automation framework. Specially for Selenium WebDriver GUI automation, Java is most popular choice and has the largest community.

Each lecture consist of a video screencast and code files

There are quizzes, homework to test your knowledge

High focus on practice and asking questions

You will also learn coding best practices

Market is never short of jobs in Java programming language, there are ample of jobs in both Java development and Automation Testing using Java.

What are you waiting for? Enroll today and learn the powerful Java language !!!

Basic knowledge
Nothing else! It’s just you, your computer and your hunger to get started today
Java concepts are covered in the course, no experience needed
Windows/MAC computer
What will you learn
You will be able to EXPLAIN, DESIGN and IMPLEMENT efficient java Programs
You will be confident to clear test automation interviews
Understand the concepts of Object Oriented Programming Language
Complete understanding of java
Expert-level knowledge of Java code (+ advanced tips and tricks used by the pros)
Suitable for beginner programmers and ideal for users who learn faster when shown
To learn more:

Fundamentos de Java: Aprende Java desde cero, sin misterios | Simpliv

Fundamentos de Java: Aprende Java desde cero, sin misterios | Simpliv

Fundamentos de Java: Aprende Java desde cero, sin misterios

Description
This is the best course to learn to program in Java in Spanish from scratch and without any experience in this fabulous programming language . This is the first course where we will study the Java Fundamentals, and we will take you step by step until you acquire the basics of the Java language and so you can start studying more advanced Java topics.

The content is divided into perfectly structured levels , each level supported by the previous one, with the aim of adding Java knowledge incrementally so that you can focus on mastering the issues little by little and gradually. So ensure the success of your Java training.

In other offer support of any doubt teaching materials included in this course Fundamentals of Java.

To make matters worse, we handle a new teaching methodology that we have called Speed ​​Learning. This methodology consists of concise videos that go directly to the point to study, complemented with eBooks with explanations and step-by-step images (which you can print, or search for any text you need, or use for your offline study), since as we know we cannot do text search within a video. In addition, our methodology includes perfectly structured and very didactic exercises, which will allow you to accelerate your eLearning learning. Without wasting time on videos where you have to watch the instructor codify an exercise, too much theory, little practice or anything like that. Our Speed ​​Learning methodology guarantees that in the shortest possible time you will acquire the necessary knowledge for the professional and professional world of Java.

The Java Fundamentals course includes the following topics of study:

Level. Java basics

Lesson 1 - Starting with Java Technology

The amazing world of Java programming
What is Java technology (from a practical approach)
Our first Java program from scratch
Lesson 2 - Variables and Operators in Java

Use of Variables in Java and what we use them for
Data types in Java and how they are classified
Operator Management and Classification in Java
Lesson 3 - Control sentences in Java

Use of the if-else structure and where to use it
Management of the switch structure and when to apply it
Lesson 4 - Cycle Management in Java

Use of the for cycle and its use
Use of the while cycle and how to apply it
Use of the do-while cycle and when to use it
Lesson 5 - Object Oriented Programming

Introduction to Object Oriented Programming (OOP)
Class Management in Java
Using Objects in Java
Lesson 6 - Functions in Java

Declaration of Methods or Functions in Java
Use and call of functions in Java
Lesson 7 - Data Management in Java

Using Arrangements in Java
Matrix Management in Java
Lesson 8 - Inheritance in Java

Inheritance Management in Java
Use of superclasses and subclasses in Java
Final Level Laboratory

Final Exercise where everything learned in this Level is integrated
At the end you get a certificate of having completed the Java Fundamentals course.

We wait for you from the other side.

Ing. Ubaldo Acosta

Founder of Global Mentoring

Passion for Java Technology

Who this course is for:

Anyone who wants to learn to program in Java
Basic knowledge
Basic knowledge of PC use
Basic operation of an operating system such as Windows, Mac or Linux
It is not required to know how to program, we will start from scratch !!!
The attitude and desire to start coding and learning Java once and for all from scratch !!!
What will you learn
Have the basics of the programming language with Java
You will know the basic syntax of the Java language
Will handle the concept of Variables and Operators in Java
We will study Object Oriented Programming with Java
You will learn Control Sentences and Cycles in Java
We will see the concept of Functions with Java
We will study the concept of Inheritance in Java
We will learn to use Arrangements in java
We will handle the concept of Matrices in Java
We will learn to Design Classes in Java
We will make a final application with everything learned in the course
To continue: